11"use client"
22
3- import { useCallback , useRef , useState } from "react"
3+ import { useCallback , useState } from "react"
44import { useRouter } from "next/navigation"
55import { z } from "zod"
66import { useQuery } from "@tanstack/react-query"
77import { useForm , FormProvider } from "react-hook-form"
88import { zodResolver } from "@hookform/resolvers/zod"
9- import fuzzysort from "fuzzysort"
109import { toast } from "sonner"
11- import { X , Rocket , Check , ChevronsUpDown , SlidersHorizontal , CircleCheck } from "lucide-react"
10+ import { X , Rocket , Check , ChevronsUpDown , SlidersHorizontal } from "lucide-react"
1211
1312import { globalSettingsSchema , providerSettingsSchema , EVALS_SETTINGS , getModelId } from "@roo-code/types"
1413
1514import { createRun } from "@/actions/runs"
1615import { getExercises } from "@/actions/exercises"
16+
1717import {
18- createRunSchema ,
1918 type CreateRun ,
20- MODEL_DEFAULT ,
19+ createRunSchema ,
2120 CONCURRENCY_MIN ,
2221 CONCURRENCY_MAX ,
2322 CONCURRENCY_DEFAULT ,
@@ -26,14 +25,19 @@ import {
2625 TIMEOUT_DEFAULT ,
2726} from "@/lib/schemas"
2827import { cn } from "@/lib/utils"
28+
2929import { useOpenRouterModels } from "@/hooks/use-open-router-models"
30+ import { useRooCodeCloudModels } from "@/hooks/use-roo-code-cloud-models"
31+
3032import {
3133 Button ,
34+ Checkbox ,
3235 FormControl ,
3336 FormField ,
3437 FormItem ,
3538 FormLabel ,
3639 FormMessage ,
40+ Input ,
3741 Textarea ,
3842 Tabs ,
3943 TabsList ,
@@ -48,36 +52,40 @@ import {
4852 Popover ,
4953 PopoverContent ,
5054 PopoverTrigger ,
51- ScrollArea ,
52- ScrollBar ,
5355 Slider ,
56+ Label ,
57+ FormDescription ,
5458} from "@/components/ui"
5559
5660import { SettingsDiff } from "./settings-diff"
5761
5862export function NewRun ( ) {
5963 const router = useRouter ( )
6064
61- const [ mode , setMode ] = useState < "openrouter" | "settings" > ( "openrouter" )
62- const [ modelSearchValue , setModelSearchValue ] = useState ( "" )
65+ const [ provider , setModelSource ] = useState < "roo" | "openrouter" | "other" > ( "roo" )
6366 const [ modelPopoverOpen , setModelPopoverOpen ] = useState ( false )
67+ const [ useNativeToolProtocol , setUseNativeToolProtocol ] = useState ( true )
6468
65- const modelSearchResultsRef = useRef < Map < string , number > > ( new Map ( ) )
66- const modelSearchValueRef = useRef ( "" )
69+ const openRouter = useOpenRouterModels ( )
70+ const rooCodeCloud = useRooCodeCloudModels ( )
71+ const models = provider === "openrouter" ? openRouter . data : rooCodeCloud . data
72+ const searchValue = provider === "openrouter" ? openRouter . searchValue : rooCodeCloud . searchValue
73+ const setSearchValue = provider === "openrouter" ? openRouter . setSearchValue : rooCodeCloud . setSearchValue
74+ const onFilter = provider === "openrouter" ? openRouter . onFilter : rooCodeCloud . onFilter
6775
68- const models = useOpenRouterModels ( )
6976 const exercises = useQuery ( { queryKey : [ "getExercises" ] , queryFn : ( ) => getExercises ( ) } )
7077
7178 const form = useForm < CreateRun > ( {
7279 resolver : zodResolver ( createRunSchema ) ,
7380 defaultValues : {
74- model : MODEL_DEFAULT ,
81+ model : "" ,
7582 description : "" ,
7683 suite : "full" ,
7784 exercises : [ ] ,
7885 settings : undefined ,
7986 concurrency : CONCURRENCY_DEFAULT ,
8087 timeout : TIMEOUT_DEFAULT ,
88+ jobToken : "" ,
8189 } ,
8290 } )
8391
@@ -93,8 +101,20 @@ export function NewRun() {
93101 const onSubmit = useCallback (
94102 async ( values : CreateRun ) => {
95103 try {
96- if ( mode === "openrouter" ) {
97- values . settings = { ...( values . settings || { } ) , openRouterModelId : model }
104+ if ( provider === "openrouter" ) {
105+ values . settings = {
106+ ...( values . settings || { } ) ,
107+ apiProvider : "openrouter" ,
108+ openRouterModelId : model ,
109+ toolProtocol : useNativeToolProtocol ? "native" : "xml" ,
110+ }
111+ } else if ( provider === "roo" ) {
112+ values . settings = {
113+ ...( values . settings || { } ) ,
114+ apiProvider : "roo" ,
115+ apiModelId : model ,
116+ toolProtocol : useNativeToolProtocol ? "native" : "xml" ,
117+ }
98118 }
99119
100120 const { id } = await createRun ( values )
@@ -103,36 +123,15 @@ export function NewRun() {
103123 toast . error ( e instanceof Error ? e . message : "An unknown error occurred." )
104124 }
105125 } ,
106- [ mode , model , router ] ,
107- )
108-
109- const onFilterModels = useCallback (
110- ( value : string , search : string ) => {
111- if ( modelSearchValueRef . current !== search ) {
112- modelSearchValueRef . current = search
113- modelSearchResultsRef . current . clear ( )
114-
115- for ( const {
116- obj : { id } ,
117- score,
118- } of fuzzysort . go ( search , models . data || [ ] , {
119- key : "name" ,
120- } ) ) {
121- modelSearchResultsRef . current . set ( id , score )
122- }
123- }
124-
125- return modelSearchResultsRef . current . get ( value ) ?? 0
126- } ,
127- [ models . data ] ,
126+ [ provider , model , router , useNativeToolProtocol ] ,
128127 )
129128
130129 const onSelectModel = useCallback (
131130 ( model : string ) => {
132131 setValue ( "model" , model )
133132 setModelPopoverOpen ( false )
134133 } ,
135- [ setValue ] ,
134+ [ setValue , setModelPopoverOpen ] ,
136135 )
137136
138137 const onImportSettings = useCallback (
@@ -160,7 +159,6 @@ export function NewRun() {
160159
161160 setValue ( "model" , getModelId ( providerSettings ) ?? "" )
162161 setValue ( "settings" , { ...EVALS_SETTINGS , ...providerSettings , ...globalSettings } )
163- setMode ( "settings" )
164162
165163 event . target . value = ""
166164 } catch ( e ) {
@@ -177,13 +175,44 @@ export function NewRun() {
177175 < form
178176 onSubmit = { form . handleSubmit ( onSubmit ) }
179177 className = "flex flex-col justify-center divide-y divide-primary *:py-5" >
180- < div className = "flex flex-row justify-between gap-4" >
181- { mode === "openrouter" && (
182- < FormField
183- control = { form . control }
184- name = "model"
185- render = { ( ) => (
186- < FormItem className = "flex-1" >
178+ < FormField
179+ control = { form . control }
180+ name = "model"
181+ render = { ( ) => (
182+ < FormItem >
183+ < Tabs
184+ value = { provider }
185+ onValueChange = { ( value ) => setModelSource ( value as "roo" | "openrouter" | "other" ) } >
186+ < TabsList className = "mb-2" >
187+ < TabsTrigger value = "roo" > Roo Code Cloud</ TabsTrigger >
188+ < TabsTrigger value = "openrouter" > OpenRouter</ TabsTrigger >
189+ < TabsTrigger value = "other" > Other</ TabsTrigger >
190+ </ TabsList >
191+ </ Tabs >
192+
193+ { provider === "other" ? (
194+ < div className = "space-y-2 overflow-auto" >
195+ < Button
196+ type = "button"
197+ variant = "secondary"
198+ onClick = { ( ) => document . getElementById ( "json-upload" ) ?. click ( ) }
199+ className = "w-full" >
200+ < SlidersHorizontal />
201+ Import Settings
202+ </ Button >
203+ < input
204+ id = "json-upload"
205+ type = "file"
206+ accept = "application/json"
207+ className = "hidden"
208+ onChange = { onImportSettings }
209+ />
210+ { settings && (
211+ < SettingsDiff defaultSettings = { EVALS_SETTINGS } customSettings = { settings } />
212+ ) }
213+ </ div >
214+ ) : (
215+ < >
187216 < Popover open = { modelPopoverOpen } onOpenChange = { setModelPopoverOpen } >
188217 < PopoverTrigger asChild >
189218 < Button
@@ -192,25 +221,23 @@ export function NewRun() {
192221 aria-expanded = { modelPopoverOpen }
193222 className = "flex items-center justify-between" >
194223 < div >
195- { models . data ?. find ( ( { id } ) => id === model ) ?. name ||
196- model ||
197- "Select OpenRouter Model" }
224+ { models ?. find ( ( { id } ) => id === model ) ?. name || `Select` }
198225 </ div >
199226 < ChevronsUpDown className = "opacity-50" />
200227 </ Button >
201228 </ PopoverTrigger >
202229 < PopoverContent className = "p-0 w-[var(--radix-popover-trigger-width)]" >
203- < Command filter = { onFilterModels } >
230+ < Command filter = { onFilter } >
204231 < CommandInput
205232 placeholder = "Search"
206- value = { modelSearchValue }
207- onValueChange = { setModelSearchValue }
233+ value = { searchValue }
234+ onValueChange = { setSearchValue }
208235 className = "h-9"
209236 />
210237 < CommandList >
211238 < CommandEmpty > No model found.</ CommandEmpty >
212239 < CommandGroup >
213- { models . data ?. map ( ( { id, name } ) => (
240+ { models ?. map ( ( { id, name } ) => (
214241 < CommandItem
215242 key = { id }
216243 value = { id }
@@ -229,45 +256,49 @@ export function NewRun() {
229256 </ Command >
230257 </ PopoverContent >
231258 </ Popover >
232- < FormMessage />
233- </ FormItem >
234- ) }
235- />
236- ) }
237259
238- < FormItem className = "flex-1" >
239- < Button
240- type = "button"
241- variant = "secondary"
242- onClick = { ( ) => document . getElementById ( "json-upload" ) ?. click ( ) } >
243- < SlidersHorizontal />
244- Import Settings
245- </ Button >
246- < input
247- id = "json-upload"
248- type = "file"
249- accept = "application/json"
250- className = "hidden"
251- onChange = { onImportSettings }
252- />
253- { settings && (
254- < ScrollArea className = "max-h-64 border rounded-sm" >
255- < >
256- < div className = "flex items-center gap-1 p-2 border-b" >
257- < CircleCheck className = "size-4 text-ring" />
258- < div className = "text-sm" >
259- Imported valid Roo Code settings. Showing differences from default
260- settings.
261- </ div >
260+ < div className = "flex items-center gap-1.5" >
261+ < Checkbox
262+ id = "native"
263+ checked = { useNativeToolProtocol }
264+ onCheckedChange = { ( checked ) =>
265+ setUseNativeToolProtocol ( checked === true )
266+ }
267+ />
268+ < Label htmlFor = "native" > Use Native Tool Calls</ Label >
262269 </ div >
263- < SettingsDiff defaultSettings = { EVALS_SETTINGS } customSettings = { settings } />
264270 </ >
265- < ScrollBar orientation = "horizontal" />
266- </ ScrollArea >
271+ ) }
272+
273+ < FormMessage />
274+ </ FormItem >
275+ ) }
276+ />
277+
278+ { provider === "roo" && (
279+ < FormField
280+ control = { form . control }
281+ name = "jobToken"
282+ render = { ( { field } ) => (
283+ < FormItem >
284+ < FormLabel > Roo Code Cloud Token</ FormLabel >
285+ < FormControl >
286+ < Input type = "password" { ...field } />
287+ </ FormControl >
288+ < FormMessage />
289+ < FormDescription >
290+ If you have access to the Roo Code Cloud repository then you can generate a
291+ token with:
292+ < br />
293+ < code className = "text-xs" >
294+ pnpm --filter @roo-code-cloud/auth production:create-job-token [org]
295+ [timeout]
296+ </ code >
297+ </ FormDescription >
298+ </ FormItem >
267299 ) }
268- < FormMessage />
269- </ FormItem >
270- </ div >
300+ />
301+ ) }
271302
272303 < FormField
273304 control = { form . control }
0 commit comments