diff --git a/apps/web/src/app/(private)/evaluations/(evaluation)/[evaluationUuid]/editor/_components/EvaluationEditor/Editor/Playground/Preview/index.tsx b/apps/web/src/app/(private)/evaluations/(evaluation)/[evaluationUuid]/editor/_components/EvaluationEditor/Editor/Playground/Preview/index.tsx
index 1c78f5232..0afefbcd3 100644
--- a/apps/web/src/app/(private)/evaluations/(evaluation)/[evaluationUuid]/editor/_components/EvaluationEditor/Editor/Playground/Preview/index.tsx
+++ b/apps/web/src/app/(private)/evaluations/(evaluation)/[evaluationUuid]/editor/_components/EvaluationEditor/Editor/Playground/Preview/index.tsx
@@ -59,7 +59,6 @@ export default function Preview({
ref={containerRef}
className='flex flex-col gap-3 h-full overflow-y-auto'
>
- Preview
{(conversation?.messages ?? [])
.filter((message) => message.role === 'assistant')
.map((message, index) => (
diff --git a/apps/web/src/app/(private)/evaluations/(evaluation)/[evaluationUuid]/editor/_components/EvaluationEditor/Editor/Playground/Variables/index.tsx b/apps/web/src/app/(private)/evaluations/(evaluation)/[evaluationUuid]/editor/_components/EvaluationEditor/Editor/Playground/Variables/index.tsx
new file mode 100644
index 000000000..585f3b1f8
--- /dev/null
+++ b/apps/web/src/app/(private)/evaluations/(evaluation)/[evaluationUuid]/editor/_components/EvaluationEditor/Editor/Playground/Variables/index.tsx
@@ -0,0 +1,273 @@
+'use client'
+
+import { ReactNode, useEffect, useState } from 'react'
+
+import { Config, readMetadata } from '@latitude-data/compiler'
+import { ProviderLogDto } from '@latitude-data/core/browser'
+import {
+ formatContext,
+ formatConversation,
+} from '@latitude-data/core/services/providerLogs/formatForEvaluation'
+import {
+ Badge,
+ Button,
+ cn,
+ CodeBlock,
+ CollapsibleBox,
+ Icon,
+ Popover,
+ Text,
+ Tooltip,
+} from '@latitude-data/web-ui'
+import useDocumentLogWithMetadata from '$/stores/documentLogWithMetadata'
+
+const PARAMETERS = [
+ 'messages',
+ 'context',
+ 'response',
+ 'prompt',
+ 'parameters',
+ 'config',
+ 'duration',
+ 'cost',
+]
+
+const VariableSection = ({
+ title,
+ content,
+ tooltip,
+ height = '36',
+ popoverContent,
+}: {
+ title: string
+ content: string
+ tooltip?: string
+ height?: string
+ popoverContent?: ReactNode
+}) => (
+
+
+
+ {title}
+ {tooltip && (
+
+
+
+ }
+ >
+ {tooltip}
+
+ )}
+
+ {popoverContent && (
+
+
+
+
+
+
+
+ {popoverContent}
+
+
+ )}
+
+
+
+)
+
+const InputSection = ({
+ title,
+ content,
+ tooltip,
+ type = 'text',
+}: {
+ title: string
+ content: string
+ tooltip: string
+ type?: string
+}) => (
+
+)
+
+export const Variables = ({
+ providerLog,
+}: {
+ providerLog?: ProviderLogDto
+}) => {
+ if (!providerLog) return null
+
+ const [config, setConfig] = useState()
+ const { data: documentLogWithMetadata } = useDocumentLogWithMetadata(
+ providerLog.documentLogUuid,
+ )
+
+ useEffect(() => {
+ const fn = async () => {
+ if (!documentLogWithMetadata) return
+
+ const metadata = await readMetadata({
+ prompt: documentLogWithMetadata!.resolvedContent,
+ })
+ setConfig(metadata.config)
+ }
+
+ fn()
+ }, [documentLogWithMetadata])
+
+ const collapsedContent = (
+
+ {PARAMETERS.map((param) => (
+
+ {param}
+
+ ))}
+
+ )
+
+ const expandedContent = (
+
+
+
+ The messages
variable contains all messages in the
+ conversation, with the following structure:
+
+
+ {`messages.all // Array of all messages
+messages.first // First message in the conversation
+messages.last // Last message in the conversation
+
+messages.user.all // Array of user messages
+messages.user.first // First user message
+messages.user.last // Last user message
+
+messages.system.all // Array of system messages
+messages.system.first // First system message
+messages.system.last // Last system message
+
+messages.assistant.all // Array of assistant messages
+messages.assistant.first // First assistant message
+messages.assistant.last // Last assistant message`}
+
+
+ You can access these properties in your prompt template using
+ JavaScript object accessor syntax. E.g:
+
+
+ {`{{messages.user.first}} // This will print the first message from the user in the conversation`}
+
+
+ }
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+
+ return (
+
+ )
+}
+
+const TooltipInfo = ({ text }: { text: string }) => {
+ return (
+
+
+
+ }
+ >
+ {text}
+
+ )
+}
diff --git a/apps/web/src/app/(private)/evaluations/(evaluation)/[evaluationUuid]/editor/_components/EvaluationEditor/Editor/Playground/index.tsx b/apps/web/src/app/(private)/evaluations/(evaluation)/[evaluationUuid]/editor/_components/EvaluationEditor/Editor/Playground/index.tsx
index 9556f24a4..67e361c58 100644
--- a/apps/web/src/app/(private)/evaluations/(evaluation)/[evaluationUuid]/editor/_components/EvaluationEditor/Editor/Playground/index.tsx
+++ b/apps/web/src/app/(private)/evaluations/(evaluation)/[evaluationUuid]/editor/_components/EvaluationEditor/Editor/Playground/index.tsx
@@ -8,7 +8,7 @@ import {
formatContext,
formatConversation,
} from '@latitude-data/core/services/providerLogs/formatForEvaluation'
-import { Badge, Icon, Input, Text } from '@latitude-data/web-ui'
+import { Button, Icon, TableBlankSlate, Text } from '@latitude-data/web-ui'
import { convertParams } from '$/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/_components/DocumentEditor/Editor/Playground'
import { ROUTES } from '$/services/routes'
import { useProviderLog } from '$/stores/providerLogs'
@@ -18,6 +18,26 @@ import { useSearchParams } from 'next/navigation'
import { Header } from '../Header'
import Chat, { EVALUATION_PARAMETERS } from './Chat'
import Preview from './Preview'
+import { Variables } from './Variables'
+
+const BlankSlate = ({ evaluation }: { evaluation: EvaluationDto }) => (
+ <>
+
+
+ Import Log
+
+ }
+ />
+ >
+)
export default function Playground({
evaluation,
@@ -58,46 +78,29 @@ export default function Playground({
}
}, [setInput, providerLog])
+ if (!providerLog) {
+ return
+ }
+
return (
<>
-
+
+
+ {providerLog && (
+
+ Import another log
+
+ )}
+
-
-
- Variables
-
- Import data from logs {' '}
-
-
-
- {Object.keys(inputs).length > 0 ? (
- Object.entries(inputs).map(([param, value], idx) => (
-
-
{{{param}}}
-
- setInput(param, e.currentTarget.value)}
- />
-
-
- ))
- ) : (
-
- No inputs. Use {{ input_name }} to insert.
-
- )}
-
-
+
+
{mode === 'preview' ? (
diff --git a/apps/web/src/app/api/documentLogs/[id]/route.ts b/apps/web/src/app/api/documentLogs/[id]/route.ts
index 522662314..f2932bf51 100644
--- a/apps/web/src/app/api/documentLogs/[id]/route.ts
+++ b/apps/web/src/app/api/documentLogs/[id]/route.ts
@@ -5,6 +5,7 @@ import { authHandler } from '$/middlewares/authHandler'
import { errorHandler } from '$/middlewares/errorHandler'
import { NextRequest, NextResponse } from 'next/server'
+// TODO: DRY with api/documentLogs/uuids/[uuid]/route.ts
export const GET = errorHandler(
authHandler(
async (
diff --git a/apps/web/src/app/api/documentLogs/uuids/[uuid]/route.ts b/apps/web/src/app/api/documentLogs/uuids/[uuid]/route.ts
new file mode 100644
index 000000000..ddafa3a5e
--- /dev/null
+++ b/apps/web/src/app/api/documentLogs/uuids/[uuid]/route.ts
@@ -0,0 +1,42 @@
+import { Workspace } from '@latitude-data/core/browser'
+import { DocumentLogsRepository } from '@latitude-data/core/repositories'
+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'
+
+// TODO: DRY with api/documentLogs/[id]/route.ts
+export const GET = errorHandler(
+ authHandler(
+ async (
+ _: NextRequest,
+ {
+ params,
+ workspace,
+ }: {
+ params: { uuid: string }
+ workspace: Workspace
+ },
+ ) => {
+ const uuid = params.uuid
+
+ if (!uuid) {
+ return NextResponse.json(
+ { message: `Document Log not found with uuid: ${uuid}` },
+ { status: 404 },
+ )
+ }
+
+ const documentLogsRepo = new DocumentLogsRepository(workspace.id)
+ const _documentLog = await documentLogsRepo
+ .findByUuid(uuid)
+ .then((res) => res.unwrap())
+ const documentLog = await fetchDocumentLogWithMetadata({
+ workspaceId: workspace.id,
+ documentLogId: _documentLog.id,
+ }).then((res) => res.unwrap())
+
+ return NextResponse.json(documentLog, { status: 200 })
+ },
+ ),
+)
diff --git a/apps/web/src/components/ProjectDocumentSelector.tsx b/apps/web/src/components/ProjectDocumentSelector.tsx
index 15f61be6b..2b38d59c4 100644
--- a/apps/web/src/components/ProjectDocumentSelector.tsx
+++ b/apps/web/src/components/ProjectDocumentSelector.tsx
@@ -36,11 +36,13 @@ export function ProjectDocumentSelector({
name='projectId'
options={projects.map((p) => ({ label: p.name, value: p.id }))}
onChange={handleProjectChange}
- defaultValue={String(defaultProjectId)}
+ defaultValue={defaultProjectId ? String(defaultProjectId) : undefined}
+ placeholder='Select a project'
/>
({
diff --git a/apps/web/src/components/layouts/AppLayout/Header/UsageIndicator/index.tsx b/apps/web/src/components/layouts/AppLayout/Header/UsageIndicator/index.tsx
index 5d98ea557..3c798ad53 100644
--- a/apps/web/src/components/layouts/AppLayout/Header/UsageIndicator/index.tsx
+++ b/apps/web/src/components/layouts/AppLayout/Header/UsageIndicator/index.tsx
@@ -8,12 +8,12 @@ import {
Button,
CircularProgress,
CircularProgressProps,
+ Popover,
Skeleton,
Text,
} from '@latitude-data/web-ui'
import useWorkspaceUsage from '$/stores/workspaceUsage'
import Link from 'next/link'
-import Popover from 'node_modules/@latitude-data/web-ui/src/ds/atoms/Popover'
function UsageIndicatorCircle({
data,
diff --git a/apps/web/src/presenters/providerLogPresenter.ts b/apps/web/src/presenters/providerLogPresenter.ts
index 1e7ebaaf4..40342f0e5 100644
--- a/apps/web/src/presenters/providerLogPresenter.ts
+++ b/apps/web/src/presenters/providerLogPresenter.ts
@@ -1,16 +1,13 @@
import { omit } from 'lodash-es'
import type { ProviderLog, ProviderLogDto } from '@latitude-data/core/browser'
+import { buildProviderLogResponse } from '@latitude-data/core/services/providerLogs/buildResponse'
export default function providerLogPresenter(
providerLog: ProviderLog,
): ProviderLogDto {
return {
...omit(providerLog, 'responseText', 'responseObject'),
- response:
- providerLog.responseText ||
- (providerLog.responseObject
- ? JSON.stringify(providerLog.responseObject)
- : ''),
+ response: buildProviderLogResponse(providerLog),
}
}
diff --git a/apps/web/src/stores/documentLogWithMetadata.ts b/apps/web/src/stores/documentLogWithMetadata.ts
new file mode 100644
index 000000000..b9d48b17c
--- /dev/null
+++ b/apps/web/src/stores/documentLogWithMetadata.ts
@@ -0,0 +1,31 @@
+import { DocumentLogWithMetadata } from '@latitude-data/core/repositories'
+import useSWR, { SWRConfiguration } from 'swr'
+
+export default function useDocumentLogWithMetadata(
+ documentLogUuid?: string | null,
+ opts?: SWRConfiguration,
+) {
+ return useSWR(
+ ['documentLogWithMetadata', documentLogUuid],
+ async () => {
+ if (!documentLogUuid) return []
+
+ const response = await fetch(
+ `/api/documentLogs/uuids/${documentLogUuid}`,
+ {
+ credentials: 'include',
+ },
+ )
+ if (!response.ok) {
+ const error = await response.json()
+
+ console.error(error)
+
+ return []
+ }
+
+ return response.json()
+ },
+ opts,
+ )
+}
diff --git a/packages/core/src/services/evaluations/run.ts b/packages/core/src/services/evaluations/run.ts
index 5c73f1ad4..d3f5e17b4 100644
--- a/packages/core/src/services/evaluations/run.ts
+++ b/packages/core/src/services/evaluations/run.ts
@@ -17,7 +17,11 @@ import { NotFoundError, Result } from '../../lib'
import { runChain } from '../chains/run'
import { computeDocumentLogWithMetadata } from '../documentLogs'
import { buildProviderApikeysMap } from '../providerApiKeys/buildMap'
-import { formatContext, formatConversation } from '../providerLogs'
+import {
+ buildProviderLogResponse,
+ formatContext,
+ formatConversation,
+} from '../providerLogs'
// Helper function to get the result schema based on evaluation type
const getResultSchema = (type: EvaluationResultableType): JSONSchema7 => {
@@ -68,7 +72,7 @@ export const runEvaluation = async (
parameters: {
messages: formatConversation(lastProviderLog),
context: formatContext(lastProviderLog),
- response: lastProviderLog.responseText,
+ response: buildProviderLogResponse(lastProviderLog),
prompt: documentLog.resolvedContent,
parameters: documentLog.parameters,
config: metadata.config,
diff --git a/packages/core/src/services/providerLogs/buildResponse.ts b/packages/core/src/services/providerLogs/buildResponse.ts
new file mode 100644
index 000000000..6849766f6
--- /dev/null
+++ b/packages/core/src/services/providerLogs/buildResponse.ts
@@ -0,0 +1,10 @@
+import { ProviderLog } from '../../browser'
+
+export function buildProviderLogResponse(providerLog: ProviderLog) {
+ return (
+ providerLog.responseText ||
+ (providerLog.responseObject
+ ? JSON.stringify(providerLog.responseObject)
+ : '')
+ )
+}
diff --git a/packages/core/src/services/providerLogs/index.ts b/packages/core/src/services/providerLogs/index.ts
index fb7753f67..419d51a7a 100644
--- a/packages/core/src/services/providerLogs/index.ts
+++ b/packages/core/src/services/providerLogs/index.ts
@@ -1,3 +1,4 @@
export * from './create'
export * from './formatForEvaluation'
export * from './addMessages'
+export * from './buildResponse'
diff --git a/packages/web-ui/src/ds/atoms/CodeBlock/index.tsx b/packages/web-ui/src/ds/atoms/CodeBlock/index.tsx
index ad3da083d..0d4ff645b 100644
--- a/packages/web-ui/src/ds/atoms/CodeBlock/index.tsx
+++ b/packages/web-ui/src/ds/atoms/CodeBlock/index.tsx
@@ -8,14 +8,17 @@ import { CopyButton } from '../CopyButton'
interface CodeBlockProps {
language: string
children: string
+ copy?: boolean
}
-export function CodeBlock({ language, children }: CodeBlockProps) {
+export function CodeBlock({ language, children, copy = true }: CodeBlockProps) {
return (
-
-
-
+ {copy && (
+
+
+
+ )}
(function Content(
) {
const props = {
...rest,
- className: cn(
- className,
- 'will-change-[transform,opacity] data-[state=open]:data-[side=top]:animate-slideDownAndFade data-[state=open]:data-[side=right]:animate-slideLeftAndFade data-[state=open]:data-[side=bottom]:animate-slideUpAndFade data-[state=open]:data-[side=left]:animate-slideRightAndFade',
- {
- 'custom-scrollbar': scrollable,
- },
- ),
+ className: cn(className, 'animate-in fade-in-0 slide-in-from-top-2', {
+ 'custom-scrollbar': scrollable,
+ }),
}
if (!inPortal) return
@@ -32,13 +28,11 @@ const PopoverContent = forwardRef(function Content(
)
})
-namespace Popover {
- export const Root = RadixPopover.Root
- export const Anchor = RadixPopover.Anchor
- export const Trigger = RadixPopover.Trigger
- export const Portal = RadixPopover.Portal
- export const Close = RadixPopover.Close
- export const Content = PopoverContent
+export const Popover = {
+ Root: RadixPopover.Root,
+ Anchor: RadixPopover.Anchor,
+ Trigger: RadixPopover.Trigger,
+ Portal: RadixPopover.Portal,
+ Close: RadixPopover.Close,
+ Content: PopoverContent,
}
-
-export default Popover
diff --git a/packages/web-ui/src/ds/atoms/Tooltip/index.tsx b/packages/web-ui/src/ds/atoms/Tooltip/index.tsx
index 409399625..fad6c93e2 100644
--- a/packages/web-ui/src/ds/atoms/Tooltip/index.tsx
+++ b/packages/web-ui/src/ds/atoms/Tooltip/index.tsx
@@ -67,7 +67,7 @@ function Tooltip({
children,
trigger,
// Provider
- delayDuration,
+ delayDuration = 350,
disableHoverableContent,
// Root
diff --git a/packages/web-ui/src/ds/molecules/CollapsibleBox/index.tsx b/packages/web-ui/src/ds/molecules/CollapsibleBox/index.tsx
new file mode 100644
index 000000000..76ff47f71
--- /dev/null
+++ b/packages/web-ui/src/ds/molecules/CollapsibleBox/index.tsx
@@ -0,0 +1,46 @@
+'use client'
+
+import React, { useState } from 'react'
+
+import { Icon, Text } from '@latitude-data/web-ui'
+
+interface CollapsibleBoxProps {
+ title: string
+ collapsedContent: React.ReactNode
+ expandedContent: React.ReactNode
+ expandedHeight?: string
+}
+
+export const CollapsibleBox: React.FC = ({
+ title,
+ collapsedContent,
+ expandedContent,
+ expandedHeight = 'auto',
+}) => {
+ const [isExpanded, setIsExpanded] = useState(false)
+
+ const toggleExpand = () => {
+ setIsExpanded(!isExpanded)
+ }
+
+ return (
+
+
+
+ {title}
+
+
+ {!isExpanded &&
{collapsedContent}
}
+
+
+
+ )
+}
diff --git a/packages/web-ui/src/ds/molecules/index.ts b/packages/web-ui/src/ds/molecules/index.ts
index 8352d95a4..d8aed1f2f 100644
--- a/packages/web-ui/src/ds/molecules/index.ts
+++ b/packages/web-ui/src/ds/molecules/index.ts
@@ -15,3 +15,4 @@ export * from './Charts'
export * from './EditableText'
export * from './Pagination'
export * from './BlankSlateWithSteps'
+export * from './CollapsibleBox'