11import { Anthropic } from "@anthropic-ai/sdk"
22import OpenAI from "openai"
33
4- import { AuthState , rooDefaultModelId , rooModels , type RooModelId } from "@roo-code/types"
4+ import { AuthState , rooDefaultModelId , rooModels , type RooModelId , type ModelInfo } from "@roo-code/types"
55import { CloudService } from "@roo-code/cloud"
66
7- import type { ApiHandlerOptions } from "../../shared/api"
7+ import type { ApiHandlerOptions , ModelRecord } from "../../shared/api"
88import { ApiStream } from "../transform/stream"
99
1010import type { ApiHandlerCreateMessageMetadata } from "../index"
1111import { DEFAULT_HEADERS } from "./constants"
1212import { BaseOpenAiCompatibleProvider } from "./base-openai-compatible-provider"
13+ import { getModels } from "../providers/fetchers/modelCache"
1314
14- export class RooHandler extends BaseOpenAiCompatibleProvider < RooModelId > {
15+ export class RooHandler extends BaseOpenAiCompatibleProvider < string > {
1516 private authStateListener ?: ( state : { state : AuthState } ) => void
17+ private mergedModels : Record < string , ModelInfo > = rooModels as Record < string , ModelInfo >
18+ private modelsLoaded = false
1619
1720 constructor ( options : ApiHandlerOptions ) {
1821 let sessionToken : string | undefined = undefined
@@ -21,18 +24,25 @@ export class RooHandler extends BaseOpenAiCompatibleProvider<RooModelId> {
2124 sessionToken = CloudService . instance . authService ?. getSessionToken ( )
2225 }
2326
27+ const baseURL = process . env . ROO_CODE_PROVIDER_URL ?? "https://api.roocode.com/proxy"
28+
2429 // Always construct the handler, even without a valid token.
2530 // The provider-proxy server will return 401 if authentication fails.
2631 super ( {
2732 ...options ,
2833 providerName : "Roo Code Cloud" ,
29- baseURL : process . env . ROO_CODE_PROVIDER_URL ?? "https://api.roocode.com/proxy/v1" ,
34+ baseURL,
3035 apiKey : sessionToken || "unauthenticated" , // Use a placeholder if no token.
3136 defaultProviderModelId : rooDefaultModelId ,
32- providerModels : rooModels ,
37+ providerModels : rooModels as Record < string , ModelInfo > ,
3338 defaultTemperature : 0.7 ,
3439 } )
3540
41+ // Load dynamic models asynchronously
42+ this . loadDynamicModels ( baseURL , sessionToken ) . catch ( ( error ) => {
43+ console . error ( "[RooHandler] Failed to load dynamic models:" , error )
44+ } )
45+
3646 if ( CloudService . hasInstance ( ) ) {
3747 const cloudService = CloudService . instance
3848
@@ -103,17 +113,37 @@ export class RooHandler extends BaseOpenAiCompatibleProvider<RooModelId> {
103113 }
104114 }
105115
116+ private async loadDynamicModels ( baseURL : string , apiKey ?: string ) : Promise < void > {
117+ try {
118+ const dynamicModels = await getModels ( {
119+ provider : "roo" ,
120+ baseUrl : baseURL ,
121+ apiKey,
122+ } )
123+ this . modelsLoaded = true
124+
125+ // Merge dynamic models with static models, preferring static model info
126+ this . mergedModels = { ...dynamicModels , ...rooModels } as Record < string , ModelInfo >
127+ } catch ( error ) {
128+ console . error ( "[RooHandler] Error loading dynamic models:" , error )
129+ // Keep using static models as fallback
130+ this . modelsLoaded = false
131+ }
132+ }
133+
106134 override getModel ( ) {
107135 const modelId = this . options . apiModelId || rooDefaultModelId
108- const modelInfo = this . providerModels [ modelId as RooModelId ] ?? this . providerModels [ rooDefaultModelId ]
136+
137+ // Try to find the model in the merged models (which includes both static and dynamic)
138+ const modelInfo = this . mergedModels [ modelId ]
109139
110140 if ( modelInfo ) {
111- return { id : modelId as RooModelId , info : modelInfo }
141+ return { id : modelId , info : modelInfo }
112142 }
113143
114144 // Return the requested model ID even if not found, with fallback info.
115145 return {
116- id : modelId as RooModelId ,
146+ id : modelId ,
117147 info : {
118148 maxTokens : 16_384 ,
119149 contextWindow : 262_144 ,
0 commit comments