mirror of
https://github.com/pacnpal/Roo-Code.git
synced 2025-12-20 12:21:13 -05:00
fix: change provider not update without done, update chatbox change provider from MrUbens
This commit is contained in:
@@ -125,6 +125,18 @@ export class ConfigManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a config exists by name
|
||||||
|
*/
|
||||||
|
async HasConfig(name: string): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
const config = await this.readConfig()
|
||||||
|
return name in config.apiConfigs
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Failed to check config existence: ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async readConfig(): Promise<ApiConfigData> {
|
private async readConfig(): Promise<ApiConfigData> {
|
||||||
try {
|
try {
|
||||||
const configKey = `${this.SCOPE_PREFIX}api_config`
|
const configKey = `${this.SCOPE_PREFIX}api_config`
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { ExtensionContext } from 'vscode'
|
import { ExtensionContext } from 'vscode'
|
||||||
import { ConfigManager } from '../ConfigManager'
|
import { ConfigManager, ApiConfigData } from '../ConfigManager'
|
||||||
import { ApiConfiguration } from '../../../shared/api'
|
import { ApiConfiguration } from '../../../shared/api'
|
||||||
import { ApiConfigData } from '../ConfigManager'
|
|
||||||
|
|
||||||
// Mock VSCode ExtensionContext
|
// Mock VSCode ExtensionContext
|
||||||
const mockSecrets = {
|
const mockSecrets = {
|
||||||
@@ -345,4 +344,41 @@ describe('ConfigManager', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('HasConfig', () => {
|
||||||
|
it('should return true for existing config', async () => {
|
||||||
|
const existingConfig: ApiConfigData = {
|
||||||
|
currentApiConfigName: 'default',
|
||||||
|
apiConfigs: {
|
||||||
|
default: {},
|
||||||
|
test: {
|
||||||
|
apiProvider: 'anthropic'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mockSecrets.get.mockResolvedValue(JSON.stringify(existingConfig))
|
||||||
|
|
||||||
|
const hasConfig = await configManager.HasConfig('test')
|
||||||
|
expect(hasConfig).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return false for non-existent config', async () => {
|
||||||
|
mockSecrets.get.mockResolvedValue(JSON.stringify({
|
||||||
|
currentApiConfigName: 'default',
|
||||||
|
apiConfigs: { default: {} }
|
||||||
|
}))
|
||||||
|
|
||||||
|
const hasConfig = await configManager.HasConfig('nonexistent')
|
||||||
|
expect(hasConfig).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should throw error if secrets storage fails', async () => {
|
||||||
|
mockSecrets.get.mockRejectedValue(new Error('Storage failed'))
|
||||||
|
|
||||||
|
await expect(configManager.HasConfig('test')).rejects.toThrow(
|
||||||
|
'Failed to check config existence: Error: Failed to read config from secrets: Error: Storage failed'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
@@ -45,7 +45,6 @@ type SecretKey =
|
|||||||
| "geminiApiKey"
|
| "geminiApiKey"
|
||||||
| "openAiNativeApiKey"
|
| "openAiNativeApiKey"
|
||||||
| "deepSeekApiKey"
|
| "deepSeekApiKey"
|
||||||
| "apiConfigPassword"
|
|
||||||
type GlobalStateKey =
|
type GlobalStateKey =
|
||||||
| "apiProvider"
|
| "apiProvider"
|
||||||
| "apiModelId"
|
| "apiModelId"
|
||||||
@@ -428,15 +427,37 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||||||
|
|
||||||
if (listApiConfig.length === 1) {
|
if (listApiConfig.length === 1) {
|
||||||
// check if first time init then sync with exist config
|
// check if first time init then sync with exist config
|
||||||
if (!checkExistKey(listApiConfig[0]) && listApiConfig[0].name === "default") {
|
if (!checkExistKey(listApiConfig[0])) {
|
||||||
const {
|
const {
|
||||||
apiConfiguration,
|
apiConfiguration,
|
||||||
} = await this.getState()
|
} = await this.getState()
|
||||||
await this.configManager.SaveConfig("default", apiConfiguration)
|
await this.configManager.SaveConfig(listApiConfig[0].name ?? "default", apiConfiguration)
|
||||||
listApiConfig[0].apiProvider = apiConfiguration.apiProvider
|
listApiConfig[0].apiProvider = apiConfiguration.apiProvider
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let currentConfigName = await this.getGlobalState("currentApiConfigName") as string
|
||||||
|
|
||||||
|
if (currentConfigName) {
|
||||||
|
if (!await this.configManager.HasConfig(currentConfigName)) {
|
||||||
|
// current config name not valid, get first config in list
|
||||||
|
await this.updateGlobalState("currentApiConfigName", listApiConfig?.[0]?.name)
|
||||||
|
if (listApiConfig?.[0]?.name) {
|
||||||
|
const apiConfig = await this.configManager.LoadConfig(listApiConfig?.[0]?.name);
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
this.updateGlobalState("listApiConfigMeta", listApiConfig),
|
||||||
|
this.postMessageToWebview({ type: "listApiConfig", listApiConfig }),
|
||||||
|
this.updateApiConfiguration(apiConfig),
|
||||||
|
])
|
||||||
|
await this.postStateToWebview()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
[
|
[
|
||||||
await this.updateGlobalState("listApiConfigMeta", listApiConfig),
|
await this.updateGlobalState("listApiConfigMeta", listApiConfig),
|
||||||
@@ -785,6 +806,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||||||
let listApiConfig = await this.configManager.ListConfig();
|
let listApiConfig = await this.configManager.ListConfig();
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
|
this.updateApiConfiguration(message.apiConfiguration),
|
||||||
this.updateGlobalState("currentApiConfigName", message.text),
|
this.updateGlobalState("currentApiConfigName", message.text),
|
||||||
this.updateGlobalState("listApiConfigMeta", listApiConfig),
|
this.updateGlobalState("listApiConfigMeta", listApiConfig),
|
||||||
])
|
])
|
||||||
@@ -800,7 +822,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||||||
if (message.values && message.apiConfiguration) {
|
if (message.values && message.apiConfiguration) {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
const {oldName, newName} = message.values
|
const { oldName, newName } = message.values
|
||||||
|
|
||||||
await this.configManager.SaveConfig(newName, message.apiConfiguration);
|
await this.configManager.SaveConfig(newName, message.apiConfiguration);
|
||||||
|
|
||||||
@@ -839,17 +861,37 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||||||
break
|
break
|
||||||
case "deleteApiConfiguration":
|
case "deleteApiConfiguration":
|
||||||
if (message.text) {
|
if (message.text) {
|
||||||
|
|
||||||
|
const answer = await vscode.window.showInformationMessage(
|
||||||
|
"What would you like to delete this api config?",
|
||||||
|
{ modal: true },
|
||||||
|
"Yes",
|
||||||
|
"No",
|
||||||
|
)
|
||||||
|
|
||||||
|
if (answer === "No" || answer === undefined) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.configManager.DeleteConfig(message.text);
|
await this.configManager.DeleteConfig(message.text);
|
||||||
let currentApiConfigName = (await this.getGlobalState("currentApiConfigName") as string) ?? "default"
|
let listApiConfig = await this.configManager.ListConfig()
|
||||||
|
let currentApiConfigName = await this.getGlobalState("currentApiConfigName")
|
||||||
|
|
||||||
if (message.text === currentApiConfigName) {
|
if (message.text === currentApiConfigName) {
|
||||||
await this.updateGlobalState("currentApiConfigName", "default")
|
await this.updateGlobalState("currentApiConfigName", listApiConfig?.[0]?.name)
|
||||||
|
if (listApiConfig?.[0]?.name) {
|
||||||
|
const apiConfig = await this.configManager.LoadConfig(listApiConfig?.[0]?.name);
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
this.updateGlobalState("listApiConfigMeta", listApiConfig),
|
||||||
|
this.updateApiConfiguration(apiConfig),
|
||||||
|
])
|
||||||
|
await this.postStateToWebview()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let listApiConfig = await this.configManager.ListConfig();
|
// this.postMessageToWebview({ type: "listApiConfig", listApiConfig })
|
||||||
await this.updateGlobalState("listApiConfigMeta", listApiConfig)
|
|
||||||
this.postMessageToWebview({ type: "listApiConfig", listApiConfig })
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error delete api configuration:", error)
|
console.error("Error delete api configuration:", error)
|
||||||
@@ -867,16 +909,6 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||||||
vscode.window.showErrorMessage("Failed to get list api configuration")
|
vscode.window.showErrorMessage("Failed to get list api configuration")
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case "setApiConfigPassword":
|
|
||||||
if (message.text) {
|
|
||||||
try {
|
|
||||||
await this.storeSecret("apiConfigPassword", message.text !== "" ? message.text : undefined)
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error set apiKey password:", error)
|
|
||||||
vscode.window.showErrorMessage("Failed to set apiKey password")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
null,
|
null,
|
||||||
@@ -1398,7 +1430,6 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||||||
requestDelaySeconds,
|
requestDelaySeconds,
|
||||||
currentApiConfigName,
|
currentApiConfigName,
|
||||||
listApiConfigMeta,
|
listApiConfigMeta,
|
||||||
apiKeyPassword
|
|
||||||
} = await this.getState()
|
} = await this.getState()
|
||||||
|
|
||||||
const allowedCommands = vscode.workspace
|
const allowedCommands = vscode.workspace
|
||||||
@@ -1435,7 +1466,6 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||||||
requestDelaySeconds: requestDelaySeconds ?? 5,
|
requestDelaySeconds: requestDelaySeconds ?? 5,
|
||||||
currentApiConfigName: currentApiConfigName ?? "default",
|
currentApiConfigName: currentApiConfigName ?? "default",
|
||||||
listApiConfigMeta: listApiConfigMeta ?? [],
|
listApiConfigMeta: listApiConfigMeta ?? [],
|
||||||
apiKeyPassword: apiKeyPassword ?? ""
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1545,7 +1575,6 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||||||
requestDelaySeconds,
|
requestDelaySeconds,
|
||||||
currentApiConfigName,
|
currentApiConfigName,
|
||||||
listApiConfigMeta,
|
listApiConfigMeta,
|
||||||
apiKeyPassword,
|
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
this.getGlobalState("apiProvider") as Promise<ApiProvider | undefined>,
|
this.getGlobalState("apiProvider") as Promise<ApiProvider | undefined>,
|
||||||
this.getGlobalState("apiModelId") as Promise<string | undefined>,
|
this.getGlobalState("apiModelId") as Promise<string | undefined>,
|
||||||
@@ -1600,7 +1629,6 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||||||
this.getGlobalState("requestDelaySeconds") as Promise<number | undefined>,
|
this.getGlobalState("requestDelaySeconds") as Promise<number | undefined>,
|
||||||
this.getGlobalState("currentApiConfigName") as Promise<string | undefined>,
|
this.getGlobalState("currentApiConfigName") as Promise<string | undefined>,
|
||||||
this.getGlobalState("listApiConfigMeta") as Promise<ApiConfigMeta[] | undefined>,
|
this.getGlobalState("listApiConfigMeta") as Promise<ApiConfigMeta[] | undefined>,
|
||||||
this.getSecret("apiConfigPassword") as Promise<string | undefined>,
|
|
||||||
])
|
])
|
||||||
|
|
||||||
let apiProvider: ApiProvider
|
let apiProvider: ApiProvider
|
||||||
@@ -1699,7 +1727,6 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||||||
requestDelaySeconds: requestDelaySeconds ?? 5,
|
requestDelaySeconds: requestDelaySeconds ?? 5,
|
||||||
currentApiConfigName: currentApiConfigName ?? "default",
|
currentApiConfigName: currentApiConfigName ?? "default",
|
||||||
listApiConfigMeta: listApiConfigMeta ?? [],
|
listApiConfigMeta: listApiConfigMeta ?? [],
|
||||||
apiKeyPassword: apiKeyPassword ?? ""
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1777,7 +1804,6 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||||||
"geminiApiKey",
|
"geminiApiKey",
|
||||||
"openAiNativeApiKey",
|
"openAiNativeApiKey",
|
||||||
"deepSeekApiKey",
|
"deepSeekApiKey",
|
||||||
"apiConfigPassword"
|
|
||||||
]
|
]
|
||||||
for (const key of secretKeys) {
|
for (const key of secretKeys) {
|
||||||
await this.storeSecret(key, undefined)
|
await this.storeSecret(key, undefined)
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ interface ApiConfigManagerProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ApiConfigManager = ({
|
const ApiConfigManager = ({
|
||||||
currentApiConfigName,
|
currentApiConfigName = "",
|
||||||
listApiConfigMeta,
|
listApiConfigMeta = [],
|
||||||
onSelectConfig,
|
onSelectConfig,
|
||||||
onDeleteConfig,
|
onDeleteConfig,
|
||||||
onRenameConfig,
|
onRenameConfig,
|
||||||
|
|||||||
@@ -46,9 +46,10 @@ interface ApiOptionsProps {
|
|||||||
showModelOptions: boolean
|
showModelOptions: boolean
|
||||||
apiErrorMessage?: string
|
apiErrorMessage?: string
|
||||||
modelIdErrorMessage?: string
|
modelIdErrorMessage?: string
|
||||||
|
onSelectProvider: (apiProvider: any) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const ApiOptions = ({ showModelOptions, apiErrorMessage, modelIdErrorMessage }: ApiOptionsProps) => {
|
const ApiOptions = ({ showModelOptions, apiErrorMessage, modelIdErrorMessage, onSelectProvider }: ApiOptionsProps) => {
|
||||||
const { apiConfiguration, setApiConfiguration, uriScheme } = useExtensionState()
|
const { apiConfiguration, setApiConfiguration, uriScheme } = useExtensionState()
|
||||||
const [ollamaModels, setOllamaModels] = useState<string[]>([])
|
const [ollamaModels, setOllamaModels] = useState<string[]>([])
|
||||||
const [lmStudioModels, setLmStudioModels] = useState<string[]>([])
|
const [lmStudioModels, setLmStudioModels] = useState<string[]>([])
|
||||||
@@ -130,7 +131,10 @@ const ApiOptions = ({ showModelOptions, apiErrorMessage, modelIdErrorMessage }:
|
|||||||
<VSCodeDropdown
|
<VSCodeDropdown
|
||||||
id="api-provider"
|
id="api-provider"
|
||||||
value={selectedProvider}
|
value={selectedProvider}
|
||||||
onChange={handleInputChange("apiProvider")}
|
onChange={(event: any) => {
|
||||||
|
onSelectProvider(event.target.value);
|
||||||
|
handleInputChange("apiProvider")(event);
|
||||||
|
}}
|
||||||
style={{ minWidth: 130, position: "relative", zIndex: OPENROUTER_MODEL_PICKER_Z_INDEX + 1 }}>
|
style={{ minWidth: 130, position: "relative", zIndex: OPENROUTER_MODEL_PICKER_Z_INDEX + 1 }}>
|
||||||
<VSCodeOption value="openrouter">OpenRouter</VSCodeOption>
|
<VSCodeOption value="openrouter">OpenRouter</VSCodeOption>
|
||||||
<VSCodeOption value="anthropic">Anthropic</VSCodeOption>
|
<VSCodeOption value="anthropic">Anthropic</VSCodeOption>
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
|
|||||||
onRenameConfig={(oldName: string, newName: string) => {
|
onRenameConfig={(oldName: string, newName: string) => {
|
||||||
vscode.postMessage({
|
vscode.postMessage({
|
||||||
type: "renameApiConfiguration",
|
type: "renameApiConfiguration",
|
||||||
values: {oldName, newName},
|
values: { oldName, newName },
|
||||||
apiConfiguration
|
apiConfiguration
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
@@ -199,6 +199,16 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
|
|||||||
showModelOptions={true}
|
showModelOptions={true}
|
||||||
apiErrorMessage={apiErrorMessage}
|
apiErrorMessage={apiErrorMessage}
|
||||||
modelIdErrorMessage={modelIdErrorMessage}
|
modelIdErrorMessage={modelIdErrorMessage}
|
||||||
|
onSelectProvider={(apiProvider: any) => {
|
||||||
|
vscode.postMessage({
|
||||||
|
type: "upsertApiConfiguration",
|
||||||
|
text: currentApiConfigName,
|
||||||
|
apiConfiguration: {
|
||||||
|
...apiConfiguration,
|
||||||
|
apiProvider: apiProvider,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ const WelcomeView = () => {
|
|||||||
<b>To get started, this extension needs an API provider for Claude 3.5 Sonnet.</b>
|
<b>To get started, this extension needs an API provider for Claude 3.5 Sonnet.</b>
|
||||||
|
|
||||||
<div style={{ marginTop: "10px" }}>
|
<div style={{ marginTop: "10px" }}>
|
||||||
<ApiOptions showModelOptions={false} />
|
<ApiOptions showModelOptions={false} onSelectProvider={() => {}} />
|
||||||
<VSCodeButton onClick={handleSubmit} disabled={disableLetsGoButton} style={{ marginTop: "3px" }}>
|
<VSCodeButton onClick={handleSubmit} disabled={disableLetsGoButton} style={{ marginTop: "3px" }}>
|
||||||
Let's go!
|
Let's go!
|
||||||
</VSCodeButton>
|
</VSCodeButton>
|
||||||
|
|||||||
Reference in New Issue
Block a user