fix: change provider not update without done, update chatbox change provider from MrUbens

This commit is contained in:
sam hoang
2025-01-07 20:16:44 +07:00
committed by Matt Rubens
parent c3fa10b367
commit 3346844584
7 changed files with 121 additions and 33 deletions

View File

@@ -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`

View File

@@ -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'
)
})
})
})

View File

@@ -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)

View File

@@ -12,8 +12,8 @@ interface ApiConfigManagerProps {
}
const ApiConfigManager = ({
currentApiConfigName,
listApiConfigMeta,
currentApiConfigName = "",
listApiConfigMeta = [],
onSelectConfig,
onDeleteConfig,
onRenameConfig,

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>