From 568bc5e5769cfb96c4cc29edd254cb6089005e82 Mon Sep 17 00:00:00 2001 From: cte Date: Mon, 24 Mar 2025 10:49:42 -0700 Subject: [PATCH] More progress --- benchmark/apps/web/package.json | 1 + benchmark/apps/web/src/app/runs/[id]/run.tsx | 50 +++++++++- .../apps/web/src/components/ui/drawer.tsx | 98 +++++++++++++++++++ benchmark/apps/web/src/components/ui/index.ts | 1 + .../web/src/components/ui/scroll-area.tsx | 7 +- .../apps/web/src/hooks/use-run-status.ts | 28 ++++-- benchmark/apps/web/src/lib/schemas.ts | 52 ++++++++-- benchmark/pnpm-lock.yaml | 18 ++++ 8 files changed, 234 insertions(+), 21 deletions(-) create mode 100644 benchmark/apps/web/src/components/ui/drawer.tsx diff --git a/benchmark/apps/web/package.json b/benchmark/apps/web/package.json index c405ef41b6..7c363679de 100644 --- a/benchmark/apps/web/package.json +++ b/benchmark/apps/web/package.json @@ -37,6 +37,7 @@ "react-use": "^17.6.0", "tailwind-merge": "^3.0.2", "tailwindcss-animate": "^1.0.7", + "vaul": "^1.1.2", "zod": "^3.24.2" }, "devDependencies": { diff --git a/benchmark/apps/web/src/app/runs/[id]/run.tsx b/benchmark/apps/web/src/app/runs/[id]/run.tsx index 48b9d60696..cc689e8a33 100644 --- a/benchmark/apps/web/src/app/runs/[id]/run.tsx +++ b/benchmark/apps/web/src/app/runs/[id]/run.tsx @@ -1,16 +1,33 @@ "use client" -import { LoaderCircle } from "lucide-react" +import { useState, useRef, useEffect } from "react" +import { LoaderCircle, RectangleEllipsis } from "lucide-react" import * as db from "@benchmark/db" import { useRunStatus } from "@/hooks/use-run-status" +import { Drawer, DrawerContent, DrawerHeader, DrawerTitle, ScrollArea } from "@/components/ui" import { TaskStatus } from "./task-status" import { ConnectionStatus } from "./connection-status" export function Run({ run }: { run: db.Run }) { - const { tasks, status, clientId, runningTaskId } = useRunStatus(run) + const { tasks, status, clientId, runningTaskId, output, outputCounts } = useRunStatus(run) + const scrollAreaRef = useRef(null) + const [selectedTask, setSelectedTask] = useState() + + useEffect(() => { + if (selectedTask) { + const scrollArea = scrollAreaRef.current + + if (scrollArea) { + scrollArea.scrollTo({ + top: scrollArea.scrollHeight, + behavior: "smooth", + }) + } + } + }, [selectedTask, outputCounts]) return ( <> @@ -29,6 +46,14 @@ export function Run({ run }: { run: db.Run }) {
{task.language}/{task.exercise}
+ {(outputCounts[task.id] ?? 0) > 0 && ( +
setSelectedTask(task)}> + +
({outputCounts[task.id]})
+
+ )} )) )} @@ -36,6 +61,27 @@ export function Run({ run }: { run: db.Run }) {
+ setSelectedTask(undefined)}> + +
+ + + {selectedTask?.language}/{selectedTask?.exercise} + + +
+ {selectedTask && ( + +
+

Tags

+ {output.get(selectedTask.id)?.map((line, i) =>
{line}
)} +
+
+ )} +
+
+
+
) } diff --git a/benchmark/apps/web/src/components/ui/drawer.tsx b/benchmark/apps/web/src/components/ui/drawer.tsx new file mode 100644 index 0000000000..756ec4ac21 --- /dev/null +++ b/benchmark/apps/web/src/components/ui/drawer.tsx @@ -0,0 +1,98 @@ +"use client" + +import * as React from "react" +import { Drawer as DrawerPrimitive } from "vaul" + +import { cn } from "@/lib/utils" + +function Drawer({ ...props }: React.ComponentProps) { + return +} + +function DrawerTrigger({ ...props }: React.ComponentProps) { + return +} + +function DrawerPortal({ ...props }: React.ComponentProps) { + return +} + +function DrawerClose({ ...props }: React.ComponentProps) { + return +} + +function DrawerOverlay({ className, ...props }: React.ComponentProps) { + return ( + + ) +} + +function DrawerContent({ className, children, ...props }: React.ComponentProps) { + return ( + + + +
+ {children} + + + ) +} + +function DrawerHeader({ className, ...props }: React.ComponentProps<"div">) { + return
+} + +function DrawerFooter({ className, ...props }: React.ComponentProps<"div">) { + return
+} + +function DrawerTitle({ className, ...props }: React.ComponentProps) { + return ( + + ) +} + +function DrawerDescription({ className, ...props }: React.ComponentProps) { + return ( + + ) +} + +export { + Drawer, + DrawerPortal, + DrawerOverlay, + DrawerTrigger, + DrawerClose, + DrawerContent, + DrawerHeader, + DrawerFooter, + DrawerTitle, + DrawerDescription, +} diff --git a/benchmark/apps/web/src/components/ui/index.ts b/benchmark/apps/web/src/components/ui/index.ts index 5c1f676dcb..45f00e3a38 100644 --- a/benchmark/apps/web/src/components/ui/index.ts +++ b/benchmark/apps/web/src/components/ui/index.ts @@ -2,6 +2,7 @@ export * from "./badge" export * from "./button" export * from "./command" export * from "./dialog" +export * from "./drawer" export * from "./form" export * from "./input" export * from "./label" diff --git a/benchmark/apps/web/src/components/ui/scroll-area.tsx b/benchmark/apps/web/src/components/ui/scroll-area.tsx index 17efdd3e7e..da5bd1c32e 100644 --- a/benchmark/apps/web/src/components/ui/scroll-area.tsx +++ b/benchmark/apps/web/src/components/ui/scroll-area.tsx @@ -5,10 +5,15 @@ import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area" import { cn } from "@/lib/utils" -function ScrollArea({ className, children, ...props }: React.ComponentProps) { +type ScrollAreaProps = React.ComponentProps & { + viewportRef?: React.RefObject +} + +function ScrollArea({ className, children, viewportRef, ...props }: ScrollAreaProps) { return ( {children} diff --git a/benchmark/apps/web/src/hooks/use-run-status.ts b/benchmark/apps/web/src/hooks/use-run-status.ts index 1c68fc8373..3216aa05f6 100644 --- a/benchmark/apps/web/src/hooks/use-run-status.ts +++ b/benchmark/apps/web/src/hooks/use-run-status.ts @@ -1,4 +1,4 @@ -import { useState, useCallback } from "react" +import { useState, useCallback, useRef } from "react" import { useQuery, keepPreviousData } from "@tanstack/react-query" import { Run } from "@benchmark/db" @@ -10,6 +10,8 @@ import { useEventSource } from "@/hooks/use-event-source" export const useRunStatus = (run: Run) => { const [clientId, setClientId] = useState() const [runningTaskId, setRunningTaskId] = useState() + const outputRef = useRef>(new Map()) + const [outputCounts, setOutputCounts] = useState>({}) const { data: tasks } = useQuery({ queryKey: ["run", run.id, runningTaskId], @@ -42,20 +44,28 @@ export const useRunStatus = (run: Run) => { if (payload.type === "Ack") { setClientId(payload.data.clientId as string) } else if (payload.type === "TaskEvent") { - const { - eventName, - data: { task }, - } = payload.data + const taskId = payload.data.data.task.id - if (eventName === "connect" || eventName === "taskStarted") { - setRunningTaskId(task.id) - } else if (eventName === "taskFinished") { + if (payload.data.eventName === "connect" || payload.data.eventName === "taskStarted") { + setRunningTaskId(taskId) + } else if (payload.data.eventName === "taskFinished") { setRunningTaskId(undefined) + } else if (payload.data.eventName === "message") { + const { text } = payload.data.data.message.message + console.log(`message: ${taskId} ->`, text) + outputRef.current.set(taskId, [...(outputRef.current.get(taskId) || []), text]) + const outputCounts: Record = {} + + for (const [taskId, messages] of outputRef.current.entries()) { + outputCounts[taskId] = messages.length + } + + setOutputCounts(outputCounts) } } }, []) const status = useEventSource({ url, onMessage }) - return { tasks, status, clientId, runningTaskId } + return { tasks, status, clientId, runningTaskId, output: outputRef.current, outputCounts } } diff --git a/benchmark/apps/web/src/lib/schemas.ts b/benchmark/apps/web/src/lib/schemas.ts index b827e64d1a..a374fe904a 100644 --- a/benchmark/apps/web/src/lib/schemas.ts +++ b/benchmark/apps/web/src/lib/schemas.ts @@ -17,6 +17,48 @@ export const createRunSchema = z export type CreateRun = z.infer +/** + * TaskEvent + */ + +export const taskEventSchema = z.discriminatedUnion("eventName", [ + z.object({ + eventName: z.literal("connect"), + data: z.object({ task: z.object({ id: z.number() }) }), + }), + z.object({ + eventName: z.literal("taskStarted"), + data: z.object({ task: z.object({ id: z.number() }) }), + }), + z.object({ + eventName: z.literal("message"), + data: z.object({ + task: z.object({ id: z.number() }), + message: z.object({ + taskId: z.string(), + action: z.enum(["created", "updated"]), + message: z.object({ + ask: z.string().optional(), + say: z.string().optional(), + partial: z.boolean(), + text: z.string(), + }), + }), + }), + }), + z.object({ + eventName: z.literal("taskTokenUsageUpdated"), + data: z.object({ + task: z.object({ id: z.number() }), + usage: z.object({}), + }), + }), + z.object({ + eventName: z.literal("taskFinished"), + data: z.object({ task: z.object({ id: z.number() }) }), + }), +]) + /** * IpcServerMessage */ @@ -28,15 +70,7 @@ export const ipcServerMessageSchema = z.discriminatedUnion("type", [ }), z.object({ type: z.literal("TaskEvent"), - data: z.object({ - eventName: z.enum(["connect", "taskStarted", "message", "taskTokenUsageUpdated", "taskFinished"]), - data: z.object({ - task: z.object({ id: z.number() }), - // message: z.object({}).optional(), - // usage: z.object({}).optional(), - // taskMetrics: z.object({}).optional(), - }), - }), + data: taskEventSchema, }), ]) diff --git a/benchmark/pnpm-lock.yaml b/benchmark/pnpm-lock.yaml index 4caf8f481c..332de94316 100644 --- a/benchmark/pnpm-lock.yaml +++ b/benchmark/pnpm-lock.yaml @@ -150,6 +150,9 @@ importers: tailwindcss-animate: specifier: ^1.0.7 version: 1.0.7(tailwindcss@4.0.14) + vaul: + specifier: ^1.1.2 + version: 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) zod: specifier: ^3.24.2 version: 3.24.2 @@ -4190,6 +4193,12 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + vaul@1.1.2: + resolution: {integrity: sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc + wcwidth@1.0.1: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} @@ -8149,6 +8158,15 @@ snapshots: util-deprecate@1.0.2: {} + vaul@1.1.2(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + dependencies: + '@radix-ui/react-dialog': 1.1.6(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + transitivePeerDependencies: + - '@types/react' + - '@types/react-dom' + wcwidth@1.0.1: dependencies: defaults: 1.0.4