|
|
@@ -1,23 +1,22 @@
|
|
|
"use client"
|
|
|
|
|
|
-import { useCallback, useRef, useState } from "react"
|
|
|
+import { useCallback, useState } from "react"
|
|
|
import { useRouter } from "next/navigation"
|
|
|
import { z } from "zod"
|
|
|
import { useQuery } from "@tanstack/react-query"
|
|
|
import { useForm, FormProvider } from "react-hook-form"
|
|
|
import { zodResolver } from "@hookform/resolvers/zod"
|
|
|
-import fuzzysort from "fuzzysort"
|
|
|
import { toast } from "sonner"
|
|
|
-import { X, Rocket, Check, ChevronsUpDown, SlidersHorizontal, CircleCheck } from "lucide-react"
|
|
|
+import { X, Rocket, Check, ChevronsUpDown, SlidersHorizontal } from "lucide-react"
|
|
|
|
|
|
import { globalSettingsSchema, providerSettingsSchema, EVALS_SETTINGS, getModelId } from "@roo-code/types"
|
|
|
|
|
|
import { createRun } from "@/actions/runs"
|
|
|
import { getExercises } from "@/actions/exercises"
|
|
|
+
|
|
|
import {
|
|
|
- createRunSchema,
|
|
|
type CreateRun,
|
|
|
- MODEL_DEFAULT,
|
|
|
+ createRunSchema,
|
|
|
CONCURRENCY_MIN,
|
|
|
CONCURRENCY_MAX,
|
|
|
CONCURRENCY_DEFAULT,
|
|
|
@@ -26,14 +25,19 @@ import {
|
|
|
TIMEOUT_DEFAULT,
|
|
|
} from "@/lib/schemas"
|
|
|
import { cn } from "@/lib/utils"
|
|
|
+
|
|
|
import { useOpenRouterModels } from "@/hooks/use-open-router-models"
|
|
|
+import { useRooCodeCloudModels } from "@/hooks/use-roo-code-cloud-models"
|
|
|
+
|
|
|
import {
|
|
|
Button,
|
|
|
+ Checkbox,
|
|
|
FormControl,
|
|
|
FormField,
|
|
|
FormItem,
|
|
|
FormLabel,
|
|
|
FormMessage,
|
|
|
+ Input,
|
|
|
Textarea,
|
|
|
Tabs,
|
|
|
TabsList,
|
|
|
@@ -48,9 +52,9 @@ import {
|
|
|
Popover,
|
|
|
PopoverContent,
|
|
|
PopoverTrigger,
|
|
|
- ScrollArea,
|
|
|
- ScrollBar,
|
|
|
Slider,
|
|
|
+ Label,
|
|
|
+ FormDescription,
|
|
|
} from "@/components/ui"
|
|
|
|
|
|
import { SettingsDiff } from "./settings-diff"
|
|
|
@@ -58,26 +62,30 @@ import { SettingsDiff } from "./settings-diff"
|
|
|
export function NewRun() {
|
|
|
const router = useRouter()
|
|
|
|
|
|
- const [mode, setMode] = useState<"openrouter" | "settings">("openrouter")
|
|
|
- const [modelSearchValue, setModelSearchValue] = useState("")
|
|
|
+ const [provider, setModelSource] = useState<"roo" | "openrouter" | "other">("roo")
|
|
|
const [modelPopoverOpen, setModelPopoverOpen] = useState(false)
|
|
|
+ const [useNativeToolProtocol, setUseNativeToolProtocol] = useState(true)
|
|
|
|
|
|
- const modelSearchResultsRef = useRef<Map<string, number>>(new Map())
|
|
|
- const modelSearchValueRef = useRef("")
|
|
|
+ const openRouter = useOpenRouterModels()
|
|
|
+ const rooCodeCloud = useRooCodeCloudModels()
|
|
|
+ const models = provider === "openrouter" ? openRouter.data : rooCodeCloud.data
|
|
|
+ const searchValue = provider === "openrouter" ? openRouter.searchValue : rooCodeCloud.searchValue
|
|
|
+ const setSearchValue = provider === "openrouter" ? openRouter.setSearchValue : rooCodeCloud.setSearchValue
|
|
|
+ const onFilter = provider === "openrouter" ? openRouter.onFilter : rooCodeCloud.onFilter
|
|
|
|
|
|
- const models = useOpenRouterModels()
|
|
|
const exercises = useQuery({ queryKey: ["getExercises"], queryFn: () => getExercises() })
|
|
|
|
|
|
const form = useForm<CreateRun>({
|
|
|
resolver: zodResolver(createRunSchema),
|
|
|
defaultValues: {
|
|
|
- model: MODEL_DEFAULT,
|
|
|
+ model: "",
|
|
|
description: "",
|
|
|
suite: "full",
|
|
|
exercises: [],
|
|
|
settings: undefined,
|
|
|
concurrency: CONCURRENCY_DEFAULT,
|
|
|
timeout: TIMEOUT_DEFAULT,
|
|
|
+ jobToken: "",
|
|
|
},
|
|
|
})
|
|
|
|
|
|
@@ -93,8 +101,20 @@ export function NewRun() {
|
|
|
const onSubmit = useCallback(
|
|
|
async (values: CreateRun) => {
|
|
|
try {
|
|
|
- if (mode === "openrouter") {
|
|
|
- values.settings = { ...(values.settings || {}), openRouterModelId: model }
|
|
|
+ if (provider === "openrouter") {
|
|
|
+ values.settings = {
|
|
|
+ ...(values.settings || {}),
|
|
|
+ apiProvider: "openrouter",
|
|
|
+ openRouterModelId: model,
|
|
|
+ toolProtocol: useNativeToolProtocol ? "native" : "xml",
|
|
|
+ }
|
|
|
+ } else if (provider === "roo") {
|
|
|
+ values.settings = {
|
|
|
+ ...(values.settings || {}),
|
|
|
+ apiProvider: "roo",
|
|
|
+ apiModelId: model,
|
|
|
+ toolProtocol: useNativeToolProtocol ? "native" : "xml",
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
const { id } = await createRun(values)
|
|
|
@@ -103,28 +123,7 @@ export function NewRun() {
|
|
|
toast.error(e instanceof Error ? e.message : "An unknown error occurred.")
|
|
|
}
|
|
|
},
|
|
|
- [mode, model, router],
|
|
|
- )
|
|
|
-
|
|
|
- const onFilterModels = useCallback(
|
|
|
- (value: string, search: string) => {
|
|
|
- if (modelSearchValueRef.current !== search) {
|
|
|
- modelSearchValueRef.current = search
|
|
|
- modelSearchResultsRef.current.clear()
|
|
|
-
|
|
|
- for (const {
|
|
|
- obj: { id },
|
|
|
- score,
|
|
|
- } of fuzzysort.go(search, models.data || [], {
|
|
|
- key: "name",
|
|
|
- })) {
|
|
|
- modelSearchResultsRef.current.set(id, score)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return modelSearchResultsRef.current.get(value) ?? 0
|
|
|
- },
|
|
|
- [models.data],
|
|
|
+ [provider, model, router, useNativeToolProtocol],
|
|
|
)
|
|
|
|
|
|
const onSelectModel = useCallback(
|
|
|
@@ -132,7 +131,7 @@ export function NewRun() {
|
|
|
setValue("model", model)
|
|
|
setModelPopoverOpen(false)
|
|
|
},
|
|
|
- [setValue],
|
|
|
+ [setValue, setModelPopoverOpen],
|
|
|
)
|
|
|
|
|
|
const onImportSettings = useCallback(
|
|
|
@@ -160,7 +159,6 @@ export function NewRun() {
|
|
|
|
|
|
setValue("model", getModelId(providerSettings) ?? "")
|
|
|
setValue("settings", { ...EVALS_SETTINGS, ...providerSettings, ...globalSettings })
|
|
|
- setMode("settings")
|
|
|
|
|
|
event.target.value = ""
|
|
|
} catch (e) {
|
|
|
@@ -177,13 +175,44 @@ export function NewRun() {
|
|
|
<form
|
|
|
onSubmit={form.handleSubmit(onSubmit)}
|
|
|
className="flex flex-col justify-center divide-y divide-primary *:py-5">
|
|
|
- <div className="flex flex-row justify-between gap-4">
|
|
|
- {mode === "openrouter" && (
|
|
|
- <FormField
|
|
|
- control={form.control}
|
|
|
- name="model"
|
|
|
- render={() => (
|
|
|
- <FormItem className="flex-1">
|
|
|
+ <FormField
|
|
|
+ control={form.control}
|
|
|
+ name="model"
|
|
|
+ render={() => (
|
|
|
+ <FormItem>
|
|
|
+ <Tabs
|
|
|
+ value={provider}
|
|
|
+ onValueChange={(value) => setModelSource(value as "roo" | "openrouter" | "other")}>
|
|
|
+ <TabsList className="mb-2">
|
|
|
+ <TabsTrigger value="roo">Roo Code Cloud</TabsTrigger>
|
|
|
+ <TabsTrigger value="openrouter">OpenRouter</TabsTrigger>
|
|
|
+ <TabsTrigger value="other">Other</TabsTrigger>
|
|
|
+ </TabsList>
|
|
|
+ </Tabs>
|
|
|
+
|
|
|
+ {provider === "other" ? (
|
|
|
+ <div className="space-y-2 overflow-auto">
|
|
|
+ <Button
|
|
|
+ type="button"
|
|
|
+ variant="secondary"
|
|
|
+ onClick={() => document.getElementById("json-upload")?.click()}
|
|
|
+ className="w-full">
|
|
|
+ <SlidersHorizontal />
|
|
|
+ Import Settings
|
|
|
+ </Button>
|
|
|
+ <input
|
|
|
+ id="json-upload"
|
|
|
+ type="file"
|
|
|
+ accept="application/json"
|
|
|
+ className="hidden"
|
|
|
+ onChange={onImportSettings}
|
|
|
+ />
|
|
|
+ {settings && (
|
|
|
+ <SettingsDiff defaultSettings={EVALS_SETTINGS} customSettings={settings} />
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ ) : (
|
|
|
+ <>
|
|
|
<Popover open={modelPopoverOpen} onOpenChange={setModelPopoverOpen}>
|
|
|
<PopoverTrigger asChild>
|
|
|
<Button
|
|
|
@@ -192,25 +221,23 @@ export function NewRun() {
|
|
|
aria-expanded={modelPopoverOpen}
|
|
|
className="flex items-center justify-between">
|
|
|
<div>
|
|
|
- {models.data?.find(({ id }) => id === model)?.name ||
|
|
|
- model ||
|
|
|
- "Select OpenRouter Model"}
|
|
|
+ {models?.find(({ id }) => id === model)?.name || `Select`}
|
|
|
</div>
|
|
|
<ChevronsUpDown className="opacity-50" />
|
|
|
</Button>
|
|
|
</PopoverTrigger>
|
|
|
<PopoverContent className="p-0 w-[var(--radix-popover-trigger-width)]">
|
|
|
- <Command filter={onFilterModels}>
|
|
|
+ <Command filter={onFilter}>
|
|
|
<CommandInput
|
|
|
placeholder="Search"
|
|
|
- value={modelSearchValue}
|
|
|
- onValueChange={setModelSearchValue}
|
|
|
+ value={searchValue}
|
|
|
+ onValueChange={setSearchValue}
|
|
|
className="h-9"
|
|
|
/>
|
|
|
<CommandList>
|
|
|
<CommandEmpty>No model found.</CommandEmpty>
|
|
|
<CommandGroup>
|
|
|
- {models.data?.map(({ id, name }) => (
|
|
|
+ {models?.map(({ id, name }) => (
|
|
|
<CommandItem
|
|
|
key={id}
|
|
|
value={id}
|
|
|
@@ -229,45 +256,49 @@ export function NewRun() {
|
|
|
</Command>
|
|
|
</PopoverContent>
|
|
|
</Popover>
|
|
|
- <FormMessage />
|
|
|
- </FormItem>
|
|
|
- )}
|
|
|
- />
|
|
|
- )}
|
|
|
|
|
|
- <FormItem className="flex-1">
|
|
|
- <Button
|
|
|
- type="button"
|
|
|
- variant="secondary"
|
|
|
- onClick={() => document.getElementById("json-upload")?.click()}>
|
|
|
- <SlidersHorizontal />
|
|
|
- Import Settings
|
|
|
- </Button>
|
|
|
- <input
|
|
|
- id="json-upload"
|
|
|
- type="file"
|
|
|
- accept="application/json"
|
|
|
- className="hidden"
|
|
|
- onChange={onImportSettings}
|
|
|
- />
|
|
|
- {settings && (
|
|
|
- <ScrollArea className="max-h-64 border rounded-sm">
|
|
|
- <>
|
|
|
- <div className="flex items-center gap-1 p-2 border-b">
|
|
|
- <CircleCheck className="size-4 text-ring" />
|
|
|
- <div className="text-sm">
|
|
|
- Imported valid Roo Code settings. Showing differences from default
|
|
|
- settings.
|
|
|
- </div>
|
|
|
+ <div className="flex items-center gap-1.5">
|
|
|
+ <Checkbox
|
|
|
+ id="native"
|
|
|
+ checked={useNativeToolProtocol}
|
|
|
+ onCheckedChange={(checked) =>
|
|
|
+ setUseNativeToolProtocol(checked === true)
|
|
|
+ }
|
|
|
+ />
|
|
|
+ <Label htmlFor="native">Use Native Tool Calls</Label>
|
|
|
</div>
|
|
|
- <SettingsDiff defaultSettings={EVALS_SETTINGS} customSettings={settings} />
|
|
|
</>
|
|
|
- <ScrollBar orientation="horizontal" />
|
|
|
- </ScrollArea>
|
|
|
+ )}
|
|
|
+
|
|
|
+ <FormMessage />
|
|
|
+ </FormItem>
|
|
|
+ )}
|
|
|
+ />
|
|
|
+
|
|
|
+ {provider === "roo" && (
|
|
|
+ <FormField
|
|
|
+ control={form.control}
|
|
|
+ name="jobToken"
|
|
|
+ render={({ field }) => (
|
|
|
+ <FormItem>
|
|
|
+ <FormLabel>Roo Code Cloud Token</FormLabel>
|
|
|
+ <FormControl>
|
|
|
+ <Input type="password" {...field} />
|
|
|
+ </FormControl>
|
|
|
+ <FormMessage />
|
|
|
+ <FormDescription>
|
|
|
+ If you have access to the Roo Code Cloud repository then you can generate a
|
|
|
+ token with:
|
|
|
+ <br />
|
|
|
+ <code className="text-xs">
|
|
|
+ pnpm --filter @roo-code-cloud/auth production:create-job-token [org]
|
|
|
+ [timeout]
|
|
|
+ </code>
|
|
|
+ </FormDescription>
|
|
|
+ </FormItem>
|
|
|
)}
|
|
|
- <FormMessage />
|
|
|
- </FormItem>
|
|
|
- </div>
|
|
|
+ />
|
|
|
+ )}
|
|
|
|
|
|
<FormField
|
|
|
control={form.control}
|