@@ -122,13 +122,15 @@ type GlobalStateKey =
122122 | "autoApprovalEnabled"
123123 | "customModes" // Array of custom modes
124124 | "unboundModelId"
125+ | "unboundModelInfo"
125126
126127export const GlobalFileNames = {
127128 apiConversationHistory : "api_conversation_history.json" ,
128129 uiMessages : "ui_messages.json" ,
129130 glamaModels : "glama_models.json" ,
130131 openRouterModels : "openrouter_models.json" ,
131132 mcpSettings : "cline_mcp_settings.json" ,
133+ unboundModels : "unbound_models.json" ,
132134}
133135
134136export class ClineProvider implements vscode . WebviewViewProvider {
@@ -665,6 +667,24 @@ export class ClineProvider implements vscode.WebviewViewProvider {
665667 }
666668 } )
667669
670+ this . readUnboundModels ( ) . then ( ( unboundModels ) => {
671+ if ( unboundModels ) {
672+ this . postMessageToWebview ( { type : "unboundModels" , unboundModels } )
673+ }
674+ } )
675+ this . refreshUnboundModels ( ) . then ( async ( unboundModels ) => {
676+ if ( unboundModels ) {
677+ const { apiConfiguration } = await this . getState ( )
678+ if ( apiConfiguration ?. unboundModelId ) {
679+ await this . updateGlobalState (
680+ "unboundModelInfo" ,
681+ unboundModels [ apiConfiguration . unboundModelId ] ,
682+ )
683+ await this . postStateToWebview ( )
684+ }
685+ }
686+ } )
687+
668688 this . configManager
669689 . listConfig ( )
670690 . then ( async ( listApiConfig ) => {
@@ -824,6 +844,9 @@ export class ClineProvider implements vscode.WebviewViewProvider {
824844 this . postMessageToWebview ( { type : "openAiModels" , openAiModels } )
825845 }
826846 break
847+ case "refreshUnboundModels" :
848+ await this . refreshUnboundModels ( )
849+ break
827850 case "openImage" :
828851 openImage ( message . text ! )
829852 break
@@ -1563,6 +1586,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
15631586 mistralApiKey,
15641587 unboundApiKey,
15651588 unboundModelId,
1589+ unboundModelInfo,
15661590 } = apiConfiguration
15671591 await this . updateGlobalState ( "apiProvider" , apiProvider )
15681592 await this . updateGlobalState ( "apiModelId" , apiModelId )
@@ -1603,6 +1627,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
16031627 await this . storeSecret ( "mistralApiKey" , mistralApiKey )
16041628 await this . storeSecret ( "unboundApiKey" , unboundApiKey )
16051629 await this . updateGlobalState ( "unboundModelId" , unboundModelId )
1630+ await this . updateGlobalState ( "unboundModelInfo" , unboundModelInfo )
16061631 if ( this . cline ) {
16071632 this . cline . api = buildApiHandler ( apiConfiguration )
16081633 }
@@ -1808,16 +1833,20 @@ export class ClineProvider implements vscode.WebviewViewProvider {
18081833 // await this.postMessageToWebview({ type: "action", action: "settingsButtonClicked" }) // bad ux if user is on welcome
18091834 }
18101835
1811- async readGlamaModels ( ) : Promise < Record < string , ModelInfo > | undefined > {
1812- const glamaModelsFilePath = path . join ( await this . ensureCacheDirectoryExists ( ) , GlobalFileNames . glamaModels )
1813- const fileExists = await fileExistsAtPath ( glamaModelsFilePath )
1836+ private async readModelsFromCache ( filename : string ) : Promise < Record < string , ModelInfo > | undefined > {
1837+ const filePath = path . join ( await this . ensureCacheDirectoryExists ( ) , filename )
1838+ const fileExists = await fileExistsAtPath ( filePath )
18141839 if ( fileExists ) {
1815- const fileContents = await fs . readFile ( glamaModelsFilePath , "utf8" )
1840+ const fileContents = await fs . readFile ( filePath , "utf8" )
18161841 return JSON . parse ( fileContents )
18171842 }
18181843 return undefined
18191844 }
18201845
1846+ async readGlamaModels ( ) : Promise < Record < string , ModelInfo > | undefined > {
1847+ return this . readModelsFromCache ( GlobalFileNames . glamaModels )
1848+ }
1849+
18211850 async refreshGlamaModels ( ) {
18221851 const glamaModelsFilePath = path . join ( await this . ensureCacheDirectoryExists ( ) , GlobalFileNames . glamaModels )
18231852
@@ -1893,16 +1922,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
18931922 }
18941923
18951924 async readOpenRouterModels ( ) : Promise < Record < string , ModelInfo > | undefined > {
1896- const openRouterModelsFilePath = path . join (
1897- await this . ensureCacheDirectoryExists ( ) ,
1898- GlobalFileNames . openRouterModels ,
1899- )
1900- const fileExists = await fileExistsAtPath ( openRouterModelsFilePath )
1901- if ( fileExists ) {
1902- const fileContents = await fs . readFile ( openRouterModelsFilePath , "utf8" )
1903- return JSON . parse ( fileContents )
1904- }
1905- return undefined
1925+ return this . readModelsFromCache ( GlobalFileNames . openRouterModels )
19061926 }
19071927
19081928 async refreshOpenRouterModels ( ) {
@@ -2017,6 +2037,46 @@ export class ClineProvider implements vscode.WebviewViewProvider {
20172037 return models
20182038 }
20192039
2040+ async readUnboundModels ( ) : Promise < Record < string , ModelInfo > | undefined > {
2041+ return this . readModelsFromCache ( GlobalFileNames . unboundModels )
2042+ }
2043+
2044+ async refreshUnboundModels ( ) {
2045+ const unboundModelsFilePath = path . join ( await this . ensureCacheDirectoryExists ( ) , GlobalFileNames . unboundModels )
2046+
2047+ const models : Record < string , ModelInfo > = { }
2048+ try {
2049+ const response = await axios . get ( "https://api.getunbound.ai/models" )
2050+
2051+ if ( response . data ) {
2052+ const rawModels : Record < string , any > = response . data
2053+
2054+ for ( const [ modelId , model ] of Object . entries ( rawModels ) ) {
2055+ models [ modelId ] = {
2056+ maxTokens : model . maxTokens ? parseInt ( model . maxTokens ) : undefined ,
2057+ contextWindow : model . contextWindow ? parseInt ( model . contextWindow ) : 0 ,
2058+ supportsImages : model . supportsImages ?? false ,
2059+ supportsPromptCache : model . supportsPromptCaching ?? false ,
2060+ supportsComputerUse : model . supportsComputerUse ?? false ,
2061+ inputPrice : model . inputTokenPrice ? parseFloat ( model . inputTokenPrice ) : undefined ,
2062+ outputPrice : model . outputTokenPrice ? parseFloat ( model . outputTokenPrice ) : undefined ,
2063+ cacheWritesPrice : model . cacheWritePrice ? parseFloat ( model . cacheWritePrice ) : undefined ,
2064+ cacheReadsPrice : model . cacheReadPrice ? parseFloat ( model . cacheReadPrice ) : undefined ,
2065+ }
2066+ }
2067+ }
2068+ await fs . writeFile ( unboundModelsFilePath , JSON . stringify ( models ) )
2069+ this . outputChannel . appendLine ( `Unbound models fetched and saved: ${ JSON . stringify ( models , null , 2 ) } ` )
2070+ } catch ( error ) {
2071+ this . outputChannel . appendLine (
2072+ `Error fetching Unbound models: ${ JSON . stringify ( error , Object . getOwnPropertyNames ( error ) , 2 ) } ` ,
2073+ )
2074+ }
2075+
2076+ await this . postMessageToWebview ( { type : "unboundModels" , unboundModels : models } )
2077+ return models
2078+ }
2079+
20202080 // Task history
20212081
20222082 async getTaskWithId ( id : string ) : Promise < {
@@ -2330,6 +2390,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
23302390 experiments ,
23312391 unboundApiKey ,
23322392 unboundModelId ,
2393+ unboundModelInfo ,
23332394 ] = await Promise . all ( [
23342395 this . getGlobalState ( "apiProvider" ) as Promise < ApiProvider | undefined > ,
23352396 this . getGlobalState ( "apiModelId" ) as Promise < string | undefined > ,
@@ -2405,6 +2466,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
24052466 this . getGlobalState ( "experiments" ) as Promise < Record < ExperimentId , boolean > | undefined > ,
24062467 this . getSecret ( "unboundApiKey" ) as Promise < string | undefined > ,
24072468 this . getGlobalState ( "unboundModelId" ) as Promise < string | undefined > ,
2469+ this . getGlobalState ( "unboundModelInfo" ) as Promise < ModelInfo | undefined > ,
24082470 ] )
24092471
24102472 let apiProvider : ApiProvider
@@ -2462,6 +2524,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
24622524 vsCodeLmModelSelector,
24632525 unboundApiKey,
24642526 unboundModelId,
2527+ unboundModelInfo,
24652528 } ,
24662529 lastShownAnnouncementId,
24672530 customInstructions,
0 commit comments