@@ -95,6 +95,7 @@ type GlobalStateKey =
9595 | "liteLlmModelId"
9696 | "qwenApiLine"
9797 | "requestyModelId"
98+ | "requestyModelInfo"
9899 | "togetherModelId"
99100 | "mcpMarketplaceCatalog"
100101 | "telemetrySetting"
@@ -103,6 +104,7 @@ export const GlobalFileNames = {
103104 apiConversationHistory : "api_conversation_history.json" ,
104105 uiMessages : "ui_messages.json" ,
105106 openRouterModels : "openrouter_models.json" ,
107+ requestyModels : "requesty_models.json" ,
106108 mcpSettings : "cline_mcp_settings.json" ,
107109 clineRules : ".clinerules" ,
108110}
@@ -497,7 +499,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
497499 } ) ,
498500 )
499501 // post last cached models in case the call to endpoint fails
500- this . readOpenRouterModels ( ) . then ( ( openRouterModels ) => {
502+ this . readDynamicProviderModels ( GlobalFileNames . openRouterModels ) . then ( ( openRouterModels ) => {
501503 if ( openRouterModels ) {
502504 this . postMessageToWebview ( {
503505 type : "openRouterModels" ,
@@ -540,6 +542,34 @@ export class ClineProvider implements vscode.WebviewViewProvider {
540542 telemetryService . updateTelemetryState ( isOptedIn )
541543 } )
542544
545+
546+ // post last cached models in case the call to endpoint fails
547+ this . readDynamicProviderModels ( GlobalFileNames . requestyModels ) . then ( ( requestyModels ) => {
548+ if ( requestyModels ) {
549+ this . postMessageToWebview ( {
550+ type : "requestyModels" ,
551+ requestyModels,
552+ } )
553+ }
554+ } )
555+
556+ // gui relies on model info to be up-to-date to provide the most accurate pricing, so we need to fetch the latest details on launch.
557+ // we do this for all users since many users switch between api providers and if they were to switch back to openrouter it would be showing outdated model info if we hadn't retrieved the latest at this point
558+ // (see normalizeApiConfiguration > openrouter)
559+ this . refreshRequestyModels ( ) . then ( async ( requestyModels ) => {
560+ if ( requestyModels ) {
561+ // update model info in state (this needs to be done here since we don't want to update state while settings is open, and we may refresh models there)
562+ const { apiConfiguration } = await this . getState ( )
563+ if ( apiConfiguration . requestyModelId ) {
564+ await this . updateGlobalState (
565+ "requestyModelInfo" ,
566+ requestyModels [ apiConfiguration . requestyModelId ] ,
567+ )
568+ await this . postStateToWebview ( )
569+ }
570+ }
571+ } )
572+
543573 break
544574 case "newTask" :
545575 // Code that should run in response to the hello message command
@@ -582,6 +612,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
582612 deepSeekApiKey,
583613 requestyApiKey,
584614 requestyModelId,
615+ requestyModelInfo,
585616 togetherApiKey,
586617 togetherModelId,
587618 qwenApiKey,
@@ -635,6 +666,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
635666 await this . updateGlobalState ( "liteLlmModelId" , liteLlmModelId )
636667 await this . updateGlobalState ( "qwenApiLine" , qwenApiLine )
637668 await this . updateGlobalState ( "requestyModelId" , requestyModelId )
669+ await this . updateGlobalState ( "requestyModelInfo" , requestyModelInfo )
638670 await this . updateGlobalState ( "togetherModelId" , togetherModelId )
639671 if ( this . cline ) {
640672 this . cline . api = buildApiHandler ( message . apiConfiguration )
@@ -731,6 +763,9 @@ export class ClineProvider implements vscode.WebviewViewProvider {
731763 case "refreshOpenRouterModels" :
732764 await this . refreshOpenRouterModels ( )
733765 break
766+ case "refreshRequestyModels" :
767+ await this . refreshRequestyModels ( )
768+ break
734769 case "refreshOpenAiModels" :
735770 const { apiConfiguration } = await this . getState ( )
736771 const openAiModels = await this . getOpenAiModels (
@@ -996,6 +1031,10 @@ export class ClineProvider implements vscode.WebviewViewProvider {
9961031 await this . updateGlobalState ( "previousModeModelId" , apiConfiguration . openRouterModelId )
9971032 await this . updateGlobalState ( "previousModeModelInfo" , apiConfiguration . openRouterModelInfo )
9981033 break
1034+ case "requesty" :
1035+ await this . updateGlobalState ( "previousModeModelId" , apiConfiguration . requestyModelId )
1036+ await this . updateGlobalState ( "previousModeModelInfo" , apiConfiguration . requestyModelInfo )
1037+ break
9991038 case "vscode-lm" :
10001039 await this . updateGlobalState ( "previousModeModelId" , apiConfiguration . vsCodeLmModelSelector )
10011040 break
@@ -1028,6 +1067,10 @@ export class ClineProvider implements vscode.WebviewViewProvider {
10281067 await this . updateGlobalState ( "openRouterModelId" , newModelId )
10291068 await this . updateGlobalState ( "openRouterModelInfo" , newModelInfo )
10301069 break
1070+ case "requesty" :
1071+ await this . updateGlobalState ( "requestyModelId" , newModelId )
1072+ await this . updateGlobalState ( "requestyModelInfo" , newModelInfo )
1073+ break
10311074 case "vscode-lm" :
10321075 await this . updateGlobalState ( "vsCodeLmModelSelector" , newModelId )
10331076 break
@@ -1500,16 +1543,61 @@ Here is the project's README to help you get started:\n\n${mcpDetails.readmeCont
15001543 return cacheDir
15011544 }
15021545
1503- async readOpenRouterModels ( ) : Promise < Record < string , ModelInfo > | undefined > {
1504- const openRouterModelsFilePath = path . join ( await this . ensureCacheDirectoryExists ( ) , GlobalFileNames . openRouterModels )
1505- const fileExists = await fileExistsAtPath ( openRouterModelsFilePath )
1546+ async readDynamicProviderModels ( filename : string ) : Promise < Record < string , ModelInfo > | undefined > {
1547+ const filePath = path . join ( await this . ensureCacheDirectoryExists ( ) , filename )
1548+ const fileExists = await fileExistsAtPath ( filePath )
15061549 if ( fileExists ) {
1507- const fileContents = await fs . readFile ( openRouterModelsFilePath , "utf8" )
1550+ const fileContents = await fs . readFile ( filePath , "utf8" )
15081551 return JSON . parse ( fileContents )
15091552 }
15101553 return undefined
15111554 }
15121555
1556+ adjustPriceToMillionTokens ( price : any ) {
1557+ if ( price ) {
1558+ return parseFloat ( price ) * 1_000_000
1559+ }
1560+ return undefined
1561+ }
1562+
1563+ async refreshRequestyModels ( ) {
1564+ const requestyModelsFilePath = path . join ( await this . ensureCacheDirectoryExists ( ) , GlobalFileNames . requestyModels )
1565+
1566+ let models : Record < string , ModelInfo > = { }
1567+ try {
1568+ const response = await axios . get ( "https://router.requesty.ai/v1/models" )
1569+ if ( response . data ?. data ) {
1570+ for ( const model of response . data . data ) {
1571+ const modelInfo : ModelInfo = {
1572+ maxTokens : model . max_output_tokens ,
1573+ contextWindow : model . context_window ,
1574+ supportsImages : model . supports_images || undefined ,
1575+ supportsComputerUse : model . supports_computer_use || undefined ,
1576+ supportsPromptCache : model . supports_caching || undefined ,
1577+ inputPrice : this . adjustPriceToMillionTokens ( model . input_price ) ,
1578+ outputPrice : this . adjustPriceToMillionTokens ( model . output_price ) ,
1579+ cacheWritesPrice : this . adjustPriceToMillionTokens ( model . caching_price ) ,
1580+ cacheReadsPrice : this . adjustPriceToMillionTokens ( model . cached_price ) ,
1581+ description : model . description ,
1582+ }
1583+ models [ model . id ] = modelInfo
1584+ }
1585+ await fs . writeFile ( requestyModelsFilePath , JSON . stringify ( models ) )
1586+ console . log ( "Requesty models fetched and saved" , models )
1587+ } else {
1588+ console . error ( "Invalid response from Requesty API" )
1589+ }
1590+ } catch ( error ) {
1591+ console . error ( "Error fetching Requesty models:" , error )
1592+ }
1593+
1594+ await this . postMessageToWebview ( {
1595+ type : "requestyModels" ,
1596+ requestyModels : models ,
1597+ } )
1598+ return models
1599+ }
1600+
15131601 async refreshOpenRouterModels ( ) {
15141602 const openRouterModelsFilePath = path . join ( await this . ensureCacheDirectoryExists ( ) , GlobalFileNames . openRouterModels )
15151603
@@ -1544,20 +1632,14 @@ Here is the project's README to help you get started:\n\n${mcpDetails.readmeCont
15441632 */
15451633 if ( response . data ?. data ) {
15461634 const rawModels = response . data . data
1547- const parsePrice = ( price : any ) => {
1548- if ( price ) {
1549- return parseFloat ( price ) * 1_000_000
1550- }
1551- return undefined
1552- }
15531635 for ( const rawModel of rawModels ) {
15541636 const modelInfo : ModelInfo = {
15551637 maxTokens : rawModel . top_provider ?. max_completion_tokens ,
15561638 contextWindow : rawModel . context_length ,
15571639 supportsImages : rawModel . architecture ?. modality ?. includes ( "image" ) ,
15581640 supportsPromptCache : false ,
1559- inputPrice : parsePrice ( rawModel . pricing ?. prompt ) ,
1560- outputPrice : parsePrice ( rawModel . pricing ?. completion ) ,
1641+ inputPrice : this . adjustPriceToMillionTokens ( rawModel . pricing ?. prompt ) ,
1642+ outputPrice : this . adjustPriceToMillionTokens ( rawModel . pricing ?. completion ) ,
15611643 description : rawModel . description ,
15621644 }
15631645
@@ -1858,6 +1940,7 @@ Here is the project's README to help you get started:\n\n${mcpDetails.readmeCont
18581940 deepSeekApiKey ,
18591941 requestyApiKey ,
18601942 requestyModelId ,
1943+ requestyModelInfo ,
18611944 togetherApiKey ,
18621945 togetherModelId ,
18631946 qwenApiKey ,
@@ -1911,6 +1994,7 @@ Here is the project's README to help you get started:\n\n${mcpDetails.readmeCont
19111994 this . getSecret ( "deepSeekApiKey" ) as Promise < string | undefined > ,
19121995 this . getSecret ( "requestyApiKey" ) as Promise < string | undefined > ,
19131996 this . getGlobalState ( "requestyModelId" ) as Promise < string | undefined > ,
1997+ this . getGlobalState ( "requestyModelInfo" ) as Promise < ModelInfo | undefined > ,
19141998 this . getSecret ( "togetherApiKey" ) as Promise < string | undefined > ,
19151999 this . getGlobalState ( "togetherModelId" ) as Promise < string | undefined > ,
19162000 this . getSecret ( "qwenApiKey" ) as Promise < string | undefined > ,
@@ -1987,6 +2071,7 @@ Here is the project's README to help you get started:\n\n${mcpDetails.readmeCont
19872071 deepSeekApiKey,
19882072 requestyApiKey,
19892073 requestyModelId,
2074+ requestyModelInfo,
19902075 togetherApiKey,
19912076 togetherModelId,
19922077 qwenApiKey,
0 commit comments