@@ -278,4 +278,95 @@ impl Provider for OpenRouterProvider {
278278 emit_debug_trace ( & self . model , & payload, & response, & usage) ;
279279 Ok ( ( message, ProviderUsage :: new ( model, usage) ) )
280280 }
281+
282+ /// Fetch supported models from OpenRouter API (only models with tool support)
283+ async fn fetch_supported_models_async ( & self ) -> Result < Option < Vec < String > > , ProviderError > {
284+ let base_url = Url :: parse ( & self . host )
285+ . map_err ( |e| ProviderError :: RequestFailed ( format ! ( "Invalid base URL: {e}" ) ) ) ?;
286+ let url = base_url. join ( "api/v1/models" ) . map_err ( |e| {
287+ ProviderError :: RequestFailed ( format ! ( "Failed to construct models URL: {e}" ) )
288+ } ) ?;
289+
290+ // Handle request failures gracefully
291+ // If the request fails, fall back to manual entry
292+ let response = match self
293+ . client
294+ . get ( url)
295+ . header ( "Authorization" , format ! ( "Bearer {}" , self . api_key) )
296+ . header ( "HTTP-Referer" , "https://block.github.io/goose" )
297+ . header ( "X-Title" , "Goose" )
298+ . send ( )
299+ . await
300+ {
301+ Ok ( response) => response,
302+ Err ( e) => {
303+ tracing:: warn!( "Failed to fetch models from OpenRouter API: {}, falling back to manual model entry" , e) ;
304+ return Ok ( None ) ;
305+ }
306+ } ;
307+
308+ // Handle JSON parsing failures gracefully
309+ let json: serde_json:: Value = match response. json ( ) . await {
310+ Ok ( json) => json,
311+ Err ( e) => {
312+ tracing:: warn!( "Failed to parse OpenRouter API response as JSON: {}, falling back to manual model entry" , e) ;
313+ return Ok ( None ) ;
314+ }
315+ } ;
316+
317+ // Check for error in response
318+ if let Some ( err_obj) = json. get ( "error" ) {
319+ let msg = err_obj
320+ . get ( "message" )
321+ . and_then ( |v| v. as_str ( ) )
322+ . unwrap_or ( "unknown error" ) ;
323+ tracing:: warn!( "OpenRouter API returned an error: {}" , msg) ;
324+ return Ok ( None ) ;
325+ }
326+
327+ let data = json. get ( "data" ) . and_then ( |v| v. as_array ( ) ) . ok_or_else ( || {
328+ ProviderError :: UsageError ( "Missing data field in JSON response" . into ( ) )
329+ } ) ?;
330+
331+ let mut models: Vec < String > = data
332+ . iter ( )
333+ . filter_map ( |model| {
334+ // Get the model ID
335+ let id = model. get ( "id" ) . and_then ( |v| v. as_str ( ) ) ?;
336+
337+ // Check if the model supports tools
338+ let supported_params =
339+ match model. get ( "supported_parameters" ) . and_then ( |v| v. as_array ( ) ) {
340+ Some ( params) => params,
341+ None => {
342+ // If supported_parameters is missing, skip this model (assume no tool support)
343+ tracing:: debug!(
344+ "Model '{}' missing supported_parameters field, skipping" ,
345+ id
346+ ) ;
347+ return None ;
348+ }
349+ } ;
350+
351+ let has_tool_support = supported_params
352+ . iter ( )
353+ . any ( |param| param. as_str ( ) == Some ( "tools" ) ) ;
354+
355+ if has_tool_support {
356+ Some ( id. to_string ( ) )
357+ } else {
358+ None
359+ }
360+ } )
361+ . collect ( ) ;
362+
363+ // If no models with tool support were found, fall back to manual entry
364+ if models. is_empty ( ) {
365+ tracing:: warn!( "No models with tool support found in OpenRouter API response, falling back to manual model entry" ) ;
366+ return Ok ( None ) ;
367+ }
368+
369+ models. sort ( ) ;
370+ Ok ( Some ( models) )
371+ }
281372}
0 commit comments