diff --git a/apps/web/src/actions/connectedEvaluations/update.test.ts b/apps/web/src/actions/connectedEvaluations/update.test.ts
new file mode 100644
index 000000000..670913d43
--- /dev/null
+++ b/apps/web/src/actions/connectedEvaluations/update.test.ts
@@ -0,0 +1,121 @@
+import { Providers } from '@latitude-data/core/browser'
+import * as factories from '@latitude-data/core/factories'
+import { beforeEach, describe, expect, it, vi } from 'vitest'
+
+import { updateConnectedEvaluationAction } from './update'
+
+const mocks = vi.hoisted(() => {
+ return {
+ getSession: vi.fn(),
+ }
+})
+
+vi.mock('$/services/auth/getSession', () => ({
+ getSession: mocks.getSession,
+}))
+
+describe('updateConnectedEvaluationAction', () => {
+ let workspace: any
+ let project: any
+ let user: any
+ let connectedEvaluation: any
+
+ beforeEach(async () => {
+ const prompt = factories.helpers.createPrompt({
+ provider: 'Latitude',
+ model: 'gpt-4o',
+ })
+ const setup = await factories.createProject({
+ providers: [{ type: Providers.OpenAI, name: 'Latitude' }],
+ name: 'Default Project',
+ documents: {
+ foo: {
+ content: prompt,
+ },
+ },
+ })
+ workspace = setup.workspace
+ project = setup.project
+ user = setup.user
+
+ // Create a connected evaluation using a factory
+ const evaluation = await factories.createLlmAsJudgeEvaluation({ workspace })
+ const { commit } = await factories.createDraft({ project, user })
+ const { documentLog } = await factories.createDocumentLog({
+ document: setup.documents[0]!,
+ commit,
+ })
+
+ connectedEvaluation = await factories.createConnectedEvaluation({
+ workspace,
+ evaluationUuid: evaluation.uuid,
+ documentUuid: documentLog.documentUuid,
+ })
+ })
+
+ describe('unauthorized', () => {
+ it('errors when the user is not authenticated', async () => {
+ mocks.getSession.mockReturnValue(null)
+
+ const [_, error] = await updateConnectedEvaluationAction({
+ id: connectedEvaluation.id,
+ data: { live: true },
+ })
+
+ expect(error!.name).toEqual('UnauthorizedError')
+ })
+ })
+
+ describe('authorized', () => {
+ beforeEach(() => {
+ mocks.getSession.mockReturnValue({
+ user,
+ workspace: { id: workspace.id, name: workspace.name },
+ })
+ })
+
+ it('successfully updates a connected evaluation', async () => {
+ const [data, error] = await updateConnectedEvaluationAction({
+ id: connectedEvaluation.id,
+ data: { live: true },
+ })
+
+ expect(error).toBeNull()
+ expect(data).toBeDefined()
+ expect(data!.id).toEqual(connectedEvaluation.id)
+ expect(data!.live).toEqual(true)
+ })
+
+ it('returns an error when the connected evaluation is not found', async () => {
+ const [_, error] = await updateConnectedEvaluationAction({
+ id: 9999, // Non-existent ID
+ data: { live: true },
+ })
+
+ expect(error).toBeDefined()
+ expect(error!.name).toEqual('NotFoundError')
+ })
+
+ it('does not update fields that are not provided', async () => {
+ const [data, _] = await updateConnectedEvaluationAction({
+ id: connectedEvaluation.id,
+ data: { live: connectedEvaluation.live }, // Provide the required 'live' field
+ })
+
+ expect(data).toBeDefined()
+ expect(data!.id).toEqual(connectedEvaluation.id)
+ expect(data!.live).toEqual(connectedEvaluation.live) // Should remain unchanged
+ })
+
+ it('handles invalid input data', async () => {
+ const [_, error] = await updateConnectedEvaluationAction({
+ id: connectedEvaluation.id,
+ // @ts-expect-error - Testing invalid input
+ data: { live: 'not a boolean' },
+ })
+
+ expect(error).toBeDefined()
+ expect(error!.name).toEqual('ZodError')
+ })
+ })
+})
diff --git a/apps/web/src/actions/connectedEvaluations/update.ts b/apps/web/src/actions/connectedEvaluations/update.ts
new file mode 100644
index 000000000..194ff9adf
--- /dev/null
+++ b/apps/web/src/actions/connectedEvaluations/update.ts
@@ -0,0 +1,33 @@
+'use server'
+
+import { ConnectedEvaluationsRepository } from '@latitude-data/core/repositories'
+import { updateConnectedEvaluation } from '@latitude-data/core/services/connectedEvaluations/update'
+import { z } from 'zod'
+
+import { authProcedure } from '../procedures'
+
+export const updateConnectedEvaluationAction = authProcedure
+ .createServerAction()
+ .input(
+ z.object({
+ id: z.number(),
+ data: z.object({
+ live: z.boolean(),
+ }),
+ }),
+ )
+ .handler(async ({ input, ctx }) => {
+ const connectedEvaluationsScope = new ConnectedEvaluationsRepository(
+ ctx.workspace.id,
+ )
+ const connectedEvaluation = await connectedEvaluationsScope
+ .find(input.id)
+ .then((r) => r.unwrap())
+
+ const result = await updateConnectedEvaluation({
+ connectedEvaluation,
+ data: input.data,
+ })
+
+ return result.unwrap()
+ })
diff --git a/apps/web/src/app/(private)/evaluations/(evaluation)/[evaluationUuid]/dashboard/_components/EvaluationStats.tsx b/apps/web/src/app/(private)/evaluations/(evaluation)/[evaluationUuid]/dashboard/_components/EvaluationStats.tsx
index cbc3e8caa..0f27f3f89 100644
--- a/apps/web/src/app/(private)/evaluations/(evaluation)/[evaluationUuid]/dashboard/_components/EvaluationStats.tsx
+++ b/apps/web/src/app/(private)/evaluations/(evaluation)/[evaluationUuid]/dashboard/_components/EvaluationStats.tsx
@@ -7,7 +7,7 @@ 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/formatUtils'
-import useConnectedDocuments from '$/stores/connectedEvaluations'
+import useConnectedDocuments from '$/stores/connectedDocuments'
export function Stat({ label, value }: { label: string; value?: string }) {
return (
diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/Actions/LiveEvaluationToggle/index.tsx b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/Actions/LiveEvaluationToggle/index.tsx
new file mode 100644
index 000000000..dad21177b
--- /dev/null
+++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/Actions/LiveEvaluationToggle/index.tsx
@@ -0,0 +1,58 @@
+import { useCallback } from 'react'
+
+import { EvaluationDto } from '@latitude-data/core/browser'
+import {
+ Label,
+ SwitchToogle,
+ useCurrentCommit,
+ useCurrentProject,
+ useToast,
+} from '@latitude-data/web-ui'
+import useConnectedEvaluations from '$/stores/connectedEvaluations'
+
+export default function LiveEvaluationToggle({
+ documentUuid,
+ evaluation,
+}: {
+ documentUuid: string
+ evaluation: EvaluationDto
+}) {
+ const { toast } = useToast()
+ const { commit } = useCurrentCommit()
+ const { project } = useCurrentProject()
+ const { data, update, isUpdating } = useConnectedEvaluations({
+ documentUuid,
+ projectId: project.id,
+ commitUuid: commit.uuid,
+ })
+ const connectedEvaluation = data.find(
+ (ev) => ev.evaluationId === evaluation.id,
+ )
+ const toggleLive = useCallback(async () => {
+ if (!connectedEvaluation) return
+
+ const live = !connectedEvaluation.live
+ const [_, error] = await update({
+ id: connectedEvaluation.id,
+ data: { live },
+ })
+ if (error) return
+
+ toast({
+ title: 'Successfully updated evaluation',
+ description: live ? 'Evaluation is now live' : 'Evaluation is now paused',
+ })
+ }, [connectedEvaluation, update])
+ if (!connectedEvaluation) return null
+
+ return (
+
+
+
+
+ )
+}
diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/Actions/index.tsx b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/Actions/index.tsx
index 7723339d7..d69178761 100644
--- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/Actions/index.tsx
+++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/Actions/index.tsx
@@ -5,6 +5,8 @@ import { Button, Icon, TableWithHeader } from '@latitude-data/web-ui'
import { ROUTES } from '$/services/routes'
import Link from 'next/link'
+import LiveEvaluationToggle from './LiveEvaluationToggle'
+
export function Actions({
evaluation,
projectId,
@@ -24,6 +26,10 @@ export function Actions({
return (
+