diff --git a/package.json b/package.json index eff0a4b..f7c0406 100644 --- a/package.json +++ b/package.json @@ -4607,8 +4607,8 @@ "category": "GitLens" }, { - "command": "gitlens.resetOpenAIKey", - "title": "Reset Stored OpenAI Key", + "command": "gitlens.resetAIKey", + "title": "Reset Stored AI Key", "category": "GitLens" }, { @@ -10110,7 +10110,7 @@ "when": "gitlens:enabled && !gitlens:readonly && !gitlens:untrusted && !gitlens:hasVirtualFolders" }, { - "command": "gitlens.resetOpenAIKey", + "command": "gitlens.resetAIKey", "when": "gitlens:enabled" } ], diff --git a/src/ai/aiProviderService.ts b/src/ai/aiProviderService.ts index 78c6c71..f41cc6f 100644 --- a/src/ai/aiProviderService.ts +++ b/src/ai/aiProviderService.ts @@ -45,6 +45,10 @@ export class AIProviderService implements Disposable { this._provider?.dispose(); } + get providerId() { + return this.provider?.id; + } + public async generateCommitMessage( repoPath: string | Uri, options?: { context?: string; progress?: ProgressOptions }, @@ -125,6 +129,16 @@ export class AIProviderService implements Disposable { } return provider.explainChanges(commit.message, diff.contents); } + + reset() { + const { providerId } = this; + if (providerId == null) return; + + void this.container.storage.deleteSecret(`gitlens.${providerId}.key`); + + void this.container.storage.delete(`confirm:ai:tos:${providerId}`); + void this.container.storage.deleteWorkspace(`confirm:ai:tos:${providerId}`); + } } async function confirmAIProviderToS(provider: AIProvider, storage: Storage): Promise { diff --git a/src/ai/anthropicProvider.ts b/src/ai/anthropicProvider.ts index 1b4efe6..1db3942 100644 --- a/src/ai/anthropicProvider.ts +++ b/src/ai/anthropicProvider.ts @@ -55,19 +55,7 @@ export class AnthropicProvider implements AIProvider { max_tokens_to_sample: 5000, stop_sequences: ['\n\nHuman:'], }; - - const rsp = await fetch('https://api.anthropic.com/v1/complete', { - headers: { - Accept: 'application/json', - Authorization: `Bearer ${apiKey}`, - 'Content-Type': 'application/json', - Client: 'anthropic-typescript/0.4.3', - 'X-API-Key': apiKey, - }, - method: 'POST', - body: JSON.stringify(request), - }); - + const rsp = await this.fetch(apiKey, request); if (!rsp.ok) { let json; try { @@ -116,19 +104,7 @@ export class AnthropicProvider implements AIProvider { stop_sequences: ['\n\nHuman:'], }; - const rsp = await fetch('https://api.anthropic.com/v1/complete', { - headers: { - Accept: 'application/json', - Authorization: `Bearer ${apiKey}`, - 'Content-Type': 'application/json', - Client: 'anthropic-typescript/0.4.3', - 'X-API-Key': apiKey, - 'anthropic-version': '2023-06-01', - }, - method: 'POST', - body: JSON.stringify(request), - }); - + const rsp = await this.fetch(apiKey, request); if (!rsp.ok) { let json; try { @@ -145,6 +121,19 @@ export class AnthropicProvider implements AIProvider { const summary = data.completion.trim(); return summary; } + + private fetch(apiKey: string, request: AnthropicCompletionRequest) { + return fetch('https://api.anthropic.com/v1/complete', { + headers: { + Accept: 'application/json', + Authorization: `Bearer ${apiKey}`, + 'Content-Type': 'application/json', + 'X-API-Key': apiKey, + }, + method: 'POST', + body: JSON.stringify(request), + }); + } } async function getApiKey(storage: Storage): Promise { @@ -165,7 +154,7 @@ async function getApiKey(storage: Storage): Promise { disposables.push( input.onDidHide(() => resolve(undefined)), input.onDidChangeValue(value => { - if (value && !/sk-[a-zA-Z0-9-_]{32,}/.test(value)) { + if (value && !/(?:sk-)?[a-zA-Z0-9-_]{32,}/.test(value)) { input.validationMessage = 'Please enter a valid Anthropic API key'; return; } @@ -173,7 +162,7 @@ async function getApiKey(storage: Storage): Promise { }), input.onDidAccept(() => { const value = input.value.trim(); - if (!value || !/sk-[a-zA-Z0-9-_]{32,}/.test(value)) { + if (!value || !/(?:sk-)?[a-zA-Z0-9-_]{32,}/.test(value)) { input.validationMessage = 'Please enter a valid Anthropic API key'; return; } diff --git a/src/ai/openaiProvider.ts b/src/ai/openaiProvider.ts index bec6125..d9cdd52 100644 --- a/src/ai/openaiProvider.ts +++ b/src/ai/openaiProvider.ts @@ -68,16 +68,7 @@ export class OpenAIProvider implements AIProvider { content: `Write a meaningful commit message for the following code changes:\n\n${code}`, }); - const rsp = await fetch(this.url, { - headers: { - Accept: 'application/json', - Authorization: `Bearer ${apiKey}`, - 'Content-Type': 'application/json', - }, - method: 'POST', - body: JSON.stringify(request), - }); - + const rsp = await this.fetch(apiKey, request); if (!rsp.ok) { debugger; if (rsp.status === 429) { @@ -130,16 +121,7 @@ export class OpenAIProvider implements AIProvider { ], }; - const rsp = await fetch(this.url, { - headers: { - Accept: 'application/json', - Authorization: `Bearer ${apiKey}`, - 'Content-Type': 'application/json', - }, - method: 'POST', - body: JSON.stringify(request), - }); - + const rsp = await this.fetch(apiKey, request); if (!rsp.ok) { debugger; if (rsp.status === 404) { @@ -159,6 +141,18 @@ export class OpenAIProvider implements AIProvider { const summary = data.choices[0].message.content.trim(); return summary; } + + private fetch(apiKey: string, request: OpenAIChatCompletionRequest) { + return fetch(this.url, { + headers: { + Accept: 'application/json', + Authorization: `Bearer ${apiKey}`, + 'Content-Type': 'application/json', + }, + method: 'POST', + body: JSON.stringify(request), + }); + } } async function getApiKey(storage: Storage): Promise { @@ -179,7 +173,7 @@ async function getApiKey(storage: Storage): Promise { disposables.push( input.onDidHide(() => resolve(undefined)), input.onDidChangeValue(value => { - if (value && !/(?:sk-)?[a-zA-Z0-9]{32}/.test(value)) { + if (value && !/(?:sk-)?[a-zA-Z0-9]{32,}/.test(value)) { input.validationMessage = 'Please enter a valid OpenAI API key'; return; } @@ -187,7 +181,7 @@ async function getApiKey(storage: Storage): Promise { }), input.onDidAccept(() => { const value = input.value.trim(); - if (!value || !/(?:sk-)?[a-zA-Z0-9]{32}/.test(value)) { + if (!value || !/(?:sk-)?[a-zA-Z0-9]{32,}/.test(value)) { input.validationMessage = 'Please enter a valid OpenAI API key'; return; } diff --git a/src/commands/generateCommitMessage.ts b/src/commands/generateCommitMessage.ts index 92fbcc1..3682027 100644 --- a/src/commands/generateCommitMessage.ts +++ b/src/commands/generateCommitMessage.ts @@ -7,7 +7,7 @@ import { showGenericErrorMessage } from '../messages'; import { getBestRepositoryOrShowPicker } from '../quickpicks/repositoryPicker'; import { command, executeCoreCommand } from '../system/command'; import { Logger } from '../system/logger'; -import { ActiveEditorCommand, Command, getCommandUri } from './base'; +import { ActiveEditorCommand, getCommandUri } from './base'; export interface GenerateCommitMessageCommandArgs { repoPath?: string; @@ -59,19 +59,3 @@ export class GenerateCommitMessageCommand extends ActiveEditorCommand { } } } - -@command() -export class ResetOpenAIKeyCommand extends Command { - constructor(private readonly container: Container) { - super(Commands.ResetOpenAIKey); - } - - execute() { - void this.container.storage.deleteSecret('gitlens.openai.key'); - void this.container.storage.deleteWithPrefix('confirm:ai:tos'); - void this.container.storage.deleteWorkspaceWithPrefix('confirm:ai:tos'); - - void this.container.storage.delete('confirm:sendToOpenAI'); - void this.container.storage.deleteWorkspace('confirm:sendToOpenAI'); - } -} diff --git a/src/commands/resets.ts b/src/commands/resets.ts index fce6233..197da78 100644 --- a/src/commands/resets.ts +++ b/src/commands/resets.ts @@ -7,6 +7,17 @@ import { configuration } from '../system/configuration'; import { Command } from './base'; @command() +export class ResetAIKeyCommand extends Command { + constructor(private readonly container: Container) { + super(Commands.ResetAIKey); + } + + execute() { + this.container.ai.reset(); + } +} + +@command() export class ResetAvatarCacheCommand extends Command { constructor(private readonly container: Container) { super(Commands.ResetAvatarCache); diff --git a/src/constants.ts b/src/constants.ts index 842b00c..6bb7a1e 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -222,7 +222,7 @@ export const enum Commands { RefreshHover = 'gitlens.refreshHover', RefreshTimelinePage = 'gitlens.timeline.refresh', ResetAvatarCache = 'gitlens.resetAvatarCache', - ResetOpenAIKey = 'gitlens.resetOpenAIKey', + ResetAIKey = 'gitlens.resetAIKey', ResetSuppressedWarnings = 'gitlens.resetSuppressedWarnings', ResetTrackedUsage = 'gitlens.resetTrackedUsage', ResetViewsLayout = 'gitlens.resetViewsLayout', @@ -714,7 +714,7 @@ export const enum SyncedStorageKeys { } export type DeprecatedGlobalStorage = { - /** @deprecated use `confirm:ai:send:openai` */ + /** @deprecated use `confirm:ai:tos:${AIProviders}` */ 'confirm:sendToOpenAI': boolean; /** @deprecated */ 'home:actions:completed': ('dismissed:welcome' | 'opened:scm')[]; @@ -762,7 +762,7 @@ export type GlobalStorage = { }; export type DeprecatedWorkspaceStorage = { - /** @deprecated use `confirm:ai:send:openai` */ + /** @deprecated use `confirm:ai:tos:${AIProviders}` */ 'confirm:sendToOpenAI': boolean; /** @deprecated */ 'graph:banners:dismissed': Record;