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> {
|
||||
try {
|
||||
const configKey = `${this.SCOPE_PREFIX}api_config`
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { ExtensionContext } from 'vscode'
|
||||
import { ConfigManager } from '../ConfigManager'
|
||||
import { ConfigManager, ApiConfigData } from '../ConfigManager'
|
||||
import { ApiConfiguration } from '../../../shared/api'
|
||||
import { ApiConfigData } from '../ConfigManager'
|
||||
|
||||
// Mock VSCode ExtensionContext
|
||||
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"
|
||||
| "openAiNativeApiKey"
|
||||
| "deepSeekApiKey"
|
||||
| "apiConfigPassword"
|
||||
type GlobalStateKey =
|
||||
| "apiProvider"
|
||||
| "apiModelId"
|
||||
@@ -428,15 +427,37 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
||||
|
||||
if (listApiConfig.length === 1) {
|
||||
// check if first time init then sync with exist config
|
||||
if (!checkExistKey(listApiConfig[0]) && listApiConfig[0].name === "default") {
|
||||
if (!checkExistKey(listApiConfig[0])) {
|
||||
const {
|
||||
apiConfiguration,
|
||||
} = await this.getState()
|
||||
await this.configManager.SaveConfig("default", apiConfiguration)
|
||||
await this.configManager.SaveConfig(listApiConfig[0].name ?? "default", apiConfiguration)
|
||||
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 this.updateGlobalState("listApiConfigMeta", listApiConfig),
|
||||
@@ -785,6 +806,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
||||
let listApiConfig = await this.configManager.ListConfig();
|
||||
|
||||
await Promise.all([
|
||||
this.updateApiConfiguration(message.apiConfiguration),
|
||||
this.updateGlobalState("currentApiConfigName", message.text),
|
||||
this.updateGlobalState("listApiConfigMeta", listApiConfig),
|
||||
])
|
||||
@@ -800,7 +822,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
||||
if (message.values && message.apiConfiguration) {
|
||||
try {
|
||||
|
||||
const {oldName, newName} = message.values
|
||||
const { oldName, newName } = message.values
|
||||
|
||||
await this.configManager.SaveConfig(newName, message.apiConfiguration);
|
||||
|
||||
@@ -839,17 +861,37 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
||||
break
|
||||
case "deleteApiConfiguration":
|
||||
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 {
|
||||
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) {
|
||||
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();
|
||||
await this.updateGlobalState("listApiConfigMeta", listApiConfig)
|
||||
this.postMessageToWebview({ type: "listApiConfig", listApiConfig })
|
||||
// this.postMessageToWebview({ type: "listApiConfig", listApiConfig })
|
||||
|
||||
} catch (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")
|
||||
}
|
||||
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,
|
||||
@@ -1398,7 +1430,6 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
||||
requestDelaySeconds,
|
||||
currentApiConfigName,
|
||||
listApiConfigMeta,
|
||||
apiKeyPassword
|
||||
} = await this.getState()
|
||||
|
||||
const allowedCommands = vscode.workspace
|
||||
@@ -1435,7 +1466,6 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
||||
requestDelaySeconds: requestDelaySeconds ?? 5,
|
||||
currentApiConfigName: currentApiConfigName ?? "default",
|
||||
listApiConfigMeta: listApiConfigMeta ?? [],
|
||||
apiKeyPassword: apiKeyPassword ?? ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1545,7 +1575,6 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
||||
requestDelaySeconds,
|
||||
currentApiConfigName,
|
||||
listApiConfigMeta,
|
||||
apiKeyPassword,
|
||||
] = await Promise.all([
|
||||
this.getGlobalState("apiProvider") as Promise<ApiProvider | 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("currentApiConfigName") as Promise<string | undefined>,
|
||||
this.getGlobalState("listApiConfigMeta") as Promise<ApiConfigMeta[] | undefined>,
|
||||
this.getSecret("apiConfigPassword") as Promise<string | undefined>,
|
||||
])
|
||||
|
||||
let apiProvider: ApiProvider
|
||||
@@ -1699,7 +1727,6 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
||||
requestDelaySeconds: requestDelaySeconds ?? 5,
|
||||
currentApiConfigName: currentApiConfigName ?? "default",
|
||||
listApiConfigMeta: listApiConfigMeta ?? [],
|
||||
apiKeyPassword: apiKeyPassword ?? ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1777,7 +1804,6 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
||||
"geminiApiKey",
|
||||
"openAiNativeApiKey",
|
||||
"deepSeekApiKey",
|
||||
"apiConfigPassword"
|
||||
]
|
||||
for (const key of secretKeys) {
|
||||
await this.storeSecret(key, undefined)
|
||||
|
||||
@@ -12,8 +12,8 @@ interface ApiConfigManagerProps {
|
||||
}
|
||||
|
||||
const ApiConfigManager = ({
|
||||
currentApiConfigName,
|
||||
listApiConfigMeta,
|
||||
currentApiConfigName = "",
|
||||
listApiConfigMeta = [],
|
||||
onSelectConfig,
|
||||
onDeleteConfig,
|
||||
onRenameConfig,
|
||||
|
||||
@@ -46,9 +46,10 @@ interface ApiOptionsProps {
|
||||
showModelOptions: boolean
|
||||
apiErrorMessage?: 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 [ollamaModels, setOllamaModels] = useState<string[]>([])
|
||||
const [lmStudioModels, setLmStudioModels] = useState<string[]>([])
|
||||
@@ -130,7 +131,10 @@ const ApiOptions = ({ showModelOptions, apiErrorMessage, modelIdErrorMessage }:
|
||||
<VSCodeDropdown
|
||||
id="api-provider"
|
||||
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 }}>
|
||||
<VSCodeOption value="openrouter">OpenRouter</VSCodeOption>
|
||||
<VSCodeOption value="anthropic">Anthropic</VSCodeOption>
|
||||
|
||||
@@ -183,7 +183,7 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
|
||||
onRenameConfig={(oldName: string, newName: string) => {
|
||||
vscode.postMessage({
|
||||
type: "renameApiConfiguration",
|
||||
values: {oldName, newName},
|
||||
values: { oldName, newName },
|
||||
apiConfiguration
|
||||
})
|
||||
}}
|
||||
@@ -199,6 +199,16 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
|
||||
showModelOptions={true}
|
||||
apiErrorMessage={apiErrorMessage}
|
||||
modelIdErrorMessage={modelIdErrorMessage}
|
||||
onSelectProvider={(apiProvider: any) => {
|
||||
vscode.postMessage({
|
||||
type: "upsertApiConfiguration",
|
||||
text: currentApiConfigName,
|
||||
apiConfiguration: {
|
||||
...apiConfiguration,
|
||||
apiProvider: apiProvider,
|
||||
}
|
||||
})
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ const WelcomeView = () => {
|
||||
<b>To get started, this extension needs an API provider for Claude 3.5 Sonnet.</b>
|
||||
|
||||
<div style={{ marginTop: "10px" }}>
|
||||
<ApiOptions showModelOptions={false} />
|
||||
<ApiOptions showModelOptions={false} onSelectProvider={() => {}} />
|
||||
<VSCodeButton onClick={handleSubmit} disabled={disableLetsGoButton} style={{ marginTop: "3px" }}>
|
||||
Let's go!
|
||||
</VSCodeButton>
|
||||
|
||||
Reference in New Issue
Block a user