Separate handling for OpenAI and DevChat API keys

- Renamed and split the API key settings in package.json into DevChat and OpenAI specific keys.
- Updated the commands for setting API keys to handle OpenAI and DevChat keys separately.
- Modified the ApiKeyManager to handle and validate OpenAI and DevChat keys separately.
- Updated the status bar view to use the DevChat access key command.
- Adjusted the tests to reflect the new handling of API keys.
This commit is contained in:
bobo.yang 2023-08-03 15:09:34 +08:00
parent c665bc881f
commit e2e761fd16
8 changed files with 93 additions and 24 deletions

View File

@ -107,10 +107,15 @@
"description": "The max number of tokens of a prompt.", "description": "The max number of tokens of a prompt.",
"when": "DevChat.llmModel == 'OpenAI'" "when": "DevChat.llmModel == 'OpenAI'"
}, },
"DevChat.API_KEY": { "DevChat.Access_Key_DevChat": {
"type": "string", "type": "string",
"default": "", "default": "",
"description": "API key for accessing the LLM model", "description": "DevChat's secret key for accessing multiple LLM models"
},
"DevChat.Api_Key_OpenAI": {
"type": "string",
"default": "",
"description": "OpenAI's secret key for accessing LLM models. (Leave blank if using DevChat's key.)",
"when": "DevChat.llmModel == 'OpenAI'" "when": "DevChat.llmModel == 'OpenAI'"
}, },
"DevChat.API_ENDPOINT": { "DevChat.API_ENDPOINT": {
@ -205,8 +210,13 @@
"title": "Create Entry" "title": "Create Entry"
}, },
{ {
"command": "DevChat.OPENAI_API_KEY", "command": "DevChat.Api_Key_OpenAI",
"title": "Input Access Key", "title": "Input OpenAI Api Key",
"category": "DevChat"
},
{
"command": "DevChat.Access_Key_DevChat",
"title": "Input DevChat Access Key",
"category": "DevChat" "category": "DevChat"
}, },
{ {

View File

@ -64,21 +64,40 @@ function registerAskForFileCommand(context: vscode.ExtensionContext) {
context.subscriptions.push(vscode.commands.registerCommand('devchat.askForFile_chinese', callback)); context.subscriptions.push(vscode.commands.registerCommand('devchat.askForFile_chinese', callback));
} }
export function registerApiKeySettingCommand(context: vscode.ExtensionContext) { export function registerOpenAiApiKeySettingCommand(context: vscode.ExtensionContext) {
const secretStorage: vscode.SecretStorage = context.secrets; const secretStorage: vscode.SecretStorage = context.secrets;
context.subscriptions.push( context.subscriptions.push(
vscode.commands.registerCommand('DevChat.OPENAI_API_KEY', async () => { vscode.commands.registerCommand('DevChat.Api_Key_OpenAI', async () => {
const passwordInput: string = await vscode.window.showInputBox({ const passwordInput: string = await vscode.window.showInputBox({
password: true, password: true,
title: "Input Access Key", title: "Input OpenAi Api Key",
placeHolder: "Set OPENAI_API_KEY (or DevChat Access Key)" placeHolder: "Set OpenAI Api Key.(Leave blank if clearing stored key.)"
}) ?? '';
if (passwordInput.trim() !== "" && !isValidApiKey(passwordInput)) {
UiUtilWrapper.showErrorMessage("Your api key is invalid!");
return ;
}
ApiKeyManager.writeApiKeySecret(passwordInput, "OpenAI");
})
);
}
export function registerDevChatApiKeySettingCommand(context: vscode.ExtensionContext) {
const secretStorage: vscode.SecretStorage = context.secrets;
context.subscriptions.push(
vscode.commands.registerCommand('DevChat.Access_Key_DevChat', async () => {
const passwordInput: string = await vscode.window.showInputBox({
password: true,
title: "Input DevChat Access Key",
placeHolder: "Set DevChat Access Key.(Leave blank if clearing stored key.)"
}) ?? ''; }) ?? '';
if (passwordInput.trim() !== "" && !isValidApiKey(passwordInput)) { if (passwordInput.trim() !== "" && !isValidApiKey(passwordInput)) {
UiUtilWrapper.showErrorMessage("Your access key is invalid!"); UiUtilWrapper.showErrorMessage("Your access key is invalid!");
return ; return ;
} }
ApiKeyManager.writeApiKeySecret(passwordInput); ApiKeyManager.writeApiKeySecret(passwordInput, "DevChat");
}) })
); );
} }

View File

@ -5,7 +5,8 @@ import {
registerAddContextCommand, registerAddContextCommand,
registerAskForCodeCommand, registerAskForCodeCommand,
registerAskForFileCommand, registerAskForFileCommand,
registerApiKeySettingCommand, registerOpenAiApiKeySettingCommand,
registerDevChatApiKeySettingCommand,
regTopicDeleteCommand, regTopicDeleteCommand,
regAddTopicCommand, regAddTopicCommand,
regDeleteSelectTopicCommand, regDeleteSelectTopicCommand,
@ -37,7 +38,8 @@ function activate(context: vscode.ExtensionContext) {
regDevChatView(context); regDevChatView(context);
regTopicView(context); regTopicView(context);
registerApiKeySettingCommand(context); registerOpenAiApiKeySettingCommand(context);
registerDevChatApiKeySettingCommand(context);
registerOpenChatPanelCommand(context); registerOpenChatPanelCommand(context);
registerAddContextCommand(context); registerAddContextCommand(context);
registerAskForCodeCommand(context); registerAskForCodeCommand(context);

View File

@ -45,9 +45,22 @@ OPENAI_API_KEY is missing from your environment or settings. Kindly input your O
} as LogEntry; } as LogEntry;
} }
export function isValidApiKey(apiKey: string) { export function isValidApiKey(apiKey: string, llmType: string = "None") {
let apiKeyStrim = apiKey.trim(); let apiKeyStrim = apiKey.trim();
if (ApiKeyManager.getKeyType(apiKeyStrim) === undefined) { const apiKeyType = ApiKeyManager.getKeyType(apiKeyStrim);
if (apiKeyType === undefined) {
return false;
}
if (llmType === "OpenAI") {
if (apiKeyType === "sk") {
return true;
}
return false;
}
if (llmType === "DevChat") {
if (apiKeyType === "DC") {
return true;
}
return false; return false;
} }
return true; return true;

View File

@ -45,7 +45,7 @@ export function createStatusBarItem(context: vscode.ExtensionContext): vscode.St
if (apiKeyStatus !== 'ready') { if (apiKeyStatus !== 'ready') {
statusBarItem.text = `$(warning)DevChat`; statusBarItem.text = `$(warning)DevChat`;
statusBarItem.tooltip = `${apiKeyStatus}`; statusBarItem.tooltip = `${apiKeyStatus}`;
statusBarItem.command = 'DevChat.OPENAI_API_KEY'; statusBarItem.command = 'DevChat.Access_Key_DevChat';
return; return;
} }

View File

@ -3,13 +3,28 @@
import { UiUtilWrapper } from './uiUtil'; import { UiUtilWrapper } from './uiUtil';
export class ApiKeyManager { export class ApiKeyManager {
static async getApiKey(): Promise<string | undefined> { static async getApiKey(llmType: string = "OpenAI"): Promise<string | undefined> {
let apiKey = await UiUtilWrapper.secretStorageGet("devchat_OPENAI_API_KEY"); let apiKey: string|undefined = undefined;
if (llmType === "OpenAI") {
apiKey = await UiUtilWrapper.secretStorageGet("openai_OPENAI_API_KEY");
}
if (!apiKey) { if (!apiKey) {
apiKey = UiUtilWrapper.getConfiguration('DevChat', 'API_KEY'); apiKey = await UiUtilWrapper.secretStorageGet("devchat_OPENAI_API_KEY");
}
if (!apiKey) {
if (llmType === "OpenAI") {
apiKey = UiUtilWrapper.getConfiguration('DevChat', 'Api_Key_OpenAI');
}
if (!apiKey) {
apiKey = UiUtilWrapper.getConfiguration('DevChat', 'Access_Key_DevChat');
}
} }
if (!apiKey) { if (!apiKey) {
apiKey = process.env.OPENAI_API_KEY; if (llmType === "OpenAI") {
apiKey = process.env.OPENAI_API_KEY;
}
} }
return apiKey; return apiKey;
} }
@ -24,8 +39,18 @@ export class ApiKeyManager {
} }
} }
static async writeApiKeySecret(apiKey: string): Promise<void> { static async writeApiKeySecret(apiKey: string, llmType: string = "Unknow"): Promise<void> {
await UiUtilWrapper.storeSecret("devchat_OPENAI_API_KEY", apiKey); if (apiKey.startsWith("sk-")) {
await UiUtilWrapper.storeSecret("openai_OPENAI_API_KEY", apiKey);
} else if (apiKey.startsWith("DC.")) {
await UiUtilWrapper.storeSecret("devchat_OPENAI_API_KEY", apiKey);
} else {
if (llmType === "OpenAI") {
await UiUtilWrapper.storeSecret("openai_OPENAI_API_KEY", apiKey);
} else if (llmType === "DevChat") {
await UiUtilWrapper.storeSecret("devchat_OPENAI_API_KEY", apiKey);
}
}
} }
static getEndPoint(apiKey: string | undefined): string | undefined { static getEndPoint(apiKey: string | undefined): string | undefined {

View File

@ -129,7 +129,7 @@ describe('sendMessageBase', () => {
workspaceFoldersFirstPathStub.returns('./'); workspaceFoldersFirstPathStub.returns('./');
getConfigurationStub.withArgs('DevChat', 'API_KEY').returns(process.env.TEST_DEVCHAT_KEY); getConfigurationStub.withArgs('DevChat', 'Access_Key_DevChat').returns(process.env.TEST_DEVCHAT_KEY);
getConfigurationStub.withArgs('DevChat', 'OpenAI.model').returns('gpt-4'); getConfigurationStub.withArgs('DevChat', 'OpenAI.model').returns('gpt-4');
getConfigurationStub.withArgs('DevChat', 'OpenAI.temperature').returns(0); getConfigurationStub.withArgs('DevChat', 'OpenAI.temperature').returns(0);
getConfigurationStub.withArgs('DevChat', 'OpenAI.stream').returns('true'); getConfigurationStub.withArgs('DevChat', 'OpenAI.stream').returns('true');
@ -156,7 +156,7 @@ describe('sendMessageBase', () => {
workspaceFoldersFirstPathStub.returns('./'); workspaceFoldersFirstPathStub.returns('./');
getConfigurationStub.withArgs('DevChat', 'API_KEY').returns('sk-KvH7ZCtHmFDCBTqH0jUv'); getConfigurationStub.withArgs('DevChat', 'Access_Key_DevChat').returns('sk-KvH7ZCtHmFDCBTqH0jUv');
getConfigurationStub.withArgs('DevChat', 'OpenAI.model').returns('gpt-4'); getConfigurationStub.withArgs('DevChat', 'OpenAI.model').returns('gpt-4');
getConfigurationStub.withArgs('DevChat', 'OpenAI.temperature').returns('0'); getConfigurationStub.withArgs('DevChat', 'OpenAI.temperature').returns('0');
getConfigurationStub.withArgs('DevChat', 'OpenAI.stream').returns('true'); getConfigurationStub.withArgs('DevChat', 'OpenAI.stream').returns('true');
@ -185,7 +185,7 @@ describe('sendMessageBase', () => {
workspaceFoldersFirstPathStub.returns('./'); workspaceFoldersFirstPathStub.returns('./');
getConfigurationStub.withArgs('DevChat', 'API_KEY').returns(process.env.TEST_DEVCHAT_KEY); getConfigurationStub.withArgs('DevChat', 'Access_Key_DevChat').returns(process.env.TEST_DEVCHAT_KEY);
getConfigurationStub.withArgs('DevChat', 'OpenAI.model').returns('gpt-4'); getConfigurationStub.withArgs('DevChat', 'OpenAI.model').returns('gpt-4');
getConfigurationStub.withArgs('DevChat', 'OpenAI.temperature').returns(0); getConfigurationStub.withArgs('DevChat', 'OpenAI.temperature').returns(0);
getConfigurationStub.withArgs('DevChat', 'OpenAI.stream').returns('true'); getConfigurationStub.withArgs('DevChat', 'OpenAI.stream').returns('true');

View File

@ -85,7 +85,7 @@ describe('ApiKeyManager', () => {
const storeSecretStub = sinon.stub(UiUtilWrapper, 'storeSecret').resolves(); const storeSecretStub = sinon.stub(UiUtilWrapper, 'storeSecret').resolves();
await ApiKeyManager.writeApiKeySecret('sk-secret'); await ApiKeyManager.writeApiKeySecret('sk-secret');
expect(storeSecretStub.calledWith('devchat_OPENAI_API_KEY', 'sk-secret')).to.be.true; expect(storeSecretStub.calledWith('openai_OPENAI_API_KEY', 'sk-secret')).to.be.true;
}); });
}); });
}); });