diff --git a/benchmark/apps/web/package.json b/benchmark/apps/web/package.json index 117d16cab4..534b7224a2 100644 --- a/benchmark/apps/web/package.json +++ b/benchmark/apps/web/package.json @@ -22,6 +22,7 @@ "@radix-ui/react-separator": "^1.1.2", "@radix-ui/react-slot": "^1.1.2", "@radix-ui/react-tabs": "^1.1.3", + "@radix-ui/react-tooltip": "^1.1.8", "@tanstack/react-query": "^5.69.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", diff --git a/benchmark/apps/web/src/app/globals.css b/benchmark/apps/web/src/app/globals.css index c3ed49b1b0..8c12f0d1d2 100644 --- a/benchmark/apps/web/src/app/globals.css +++ b/benchmark/apps/web/src/app/globals.css @@ -48,14 +48,14 @@ --popover-foreground: oklch(0.985 0 0); --primary: oklch(29.33% 0.0295 276.18); --primary-foreground: var(--accent); - --secondary: oklch(0.269 0 0); - --secondary-foreground: oklch(0.985 0 0); + --secondary: var(--primary); + --secondary-foreground: var(--foreground); --muted: oklch(28.27% 0.0207 273.06); - --muted-foreground: oklch(75.15% 0.0477 278.41); + --muted-foreground: oklch(75.15% 0.0477 278.41 / 75%); --accent: oklch(70.21% 0.1813 328.71); - --accent-foreground: rgba(0, 0, 0, 0.5); + --accent-foreground: oklch(1 0 0 / 75%); --destructive: oklch(72.14% 0.1616 15.49); - --border: oklch(29.33% 0.0295 276.18); + --border: var(--primary); --input: var(--primary); --ring: oklch(83.63% 0.1259 176.52); --chart-1: oklch(0.488 0.243 264.376); diff --git a/benchmark/apps/web/src/app/runs/new/new-run.tsx b/benchmark/apps/web/src/app/runs/new/new-run.tsx index 587ab91737..0750bff80e 100644 --- a/benchmark/apps/web/src/app/runs/new/new-run.tsx +++ b/benchmark/apps/web/src/app/runs/new/new-run.tsx @@ -1,11 +1,13 @@ "use client" -import { useCallback, useRef, useState } from "react" +import { Fragment, useCallback, useRef, useState } from "react" import { useRouter } from "next/navigation" import { useForm, FormProvider } from "react-hook-form" import { zodResolver } from "@hookform/resolvers/zod" import fuzzysort from "fuzzysort" -import { X, Rocket, Check, ChevronsUpDown } from "lucide-react" +import { X, Rocket, Check, ChevronsUpDown, HardDriveUpload, CircleCheck } from "lucide-react" + +import { ExperimentId, Experiments, globalSettingsSchema, rooCodeDefaults } from "@benchmark/types" import { createRun } from "@/lib/server/runs" import { createRunSchema as formSchema, type CreateRun as FormValues } from "@/lib/schemas" @@ -18,6 +20,7 @@ import { FormField, FormItem, FormLabel, + FormDescription, FormMessage, Textarea, Tabs, @@ -33,7 +36,16 @@ import { Popover, PopoverContent, PopoverTrigger, + ScrollArea, } from "@/components/ui" +import { z } from "zod" +import { SettingsDiff } from "./settings-diff" + +const recommendedModels = [ + "anthropic/claude-3.7-sonnet", + "anthropic/claude-3.7-sonnet:thinking", + "google/gemini-2.0-flash-001", +] export function NewRun() { const router = useRouter() @@ -53,38 +65,33 @@ export function NewRun() { description: "", suite: "full", exercises: [], + settings: undefined, }, }) const { setValue, + setError, + clearErrors, watch, formState: { isSubmitting }, } = form - const [model, suite] = watch(["model", "suite"]) - - const selectModel = useCallback( - (model: string) => { - setValue("model", model) - setModelPopoverOpen(false) - }, - [setValue], - ) + const [model, suite, settings] = watch(["model", "suite", "settings"]) const onSubmit = useCallback( async (data: FormValues) => { try { const { id } = await createRun(data) router.push(`/runs/${id}`) - } catch (_) { + } catch (_e) { // Surface error. } }, [router], ) - const onFilter = useCallback( + const onFilterModels = useCallback( (value: string, search: string) => { if (modelSearchValueRef.current !== search) { modelSearchValueRef.current = search @@ -105,16 +112,41 @@ export function NewRun() { [models.data], ) - const recommendedModels = [ - "anthropic/claude-3.7-sonnet", - "anthropic/claude-3.7-sonnet:thinking", - "google/gemini-2.0-flash-001", - ] + const onSelectModel = useCallback( + (model: string) => { + setValue("model", model) + setModelPopoverOpen(false) + }, + [setValue], + ) + + const onImportSettings = useCallback( + async (event: React.ChangeEvent) => { + const file = event.target.files?.[0] + + if (!file) { + return + } + + clearErrors("settings") + + try { + const result = z.object({ globalSettings: globalSettingsSchema }).parse(JSON.parse(await file.text())) + setValue("settings", result.globalSettings) + event.target.value = "" + } catch (_error) { + setError("settings", { message: "Error parsing JSON file. Please check the file format." }) + } + }, + [clearErrors, setError, setValue], + ) return ( <> -
+ - + No model found. {models.data?.map(({ id, name }) => ( - + {name} -
-
Recommended:
+ + Recommended: {recommendedModels.map((modelId) => ( -
- -
+ ))} -
+ )} /> + + Import Settings + + + {settings ? ( + + <> +
+ +
+ Imported valid Roo Code settings. Showing differences from default settings. +
+
+ + +
+ ) : ( + + Fully configure how Roo Code for this run using a settings file that was exported by Roo + Code. + + )} + +
+ ( - Exercise Suite + Exercises setValue("suite", value as "full" | "partial")}> - - Full - Partial + + All + Some - - )} - /> - - {suite === "partial" && ( - ( - - Exercises + {suite === "partial" && ( ({ value: path, label: path })) || []} onValueChange={(value) => setValue("exercises", value)} @@ -214,18 +272,18 @@ export function NewRun() { variant="inverted" maxCount={4} /> - - - )} - /> - )} + )} + + + )} + /> ( - Description + Description / Notes