mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-12-19 07:32:22 +03:00
Some suggestions for improvement
This commit is contained in:
parent
6c2d98d2f4
commit
c90d887ab7
@ -20,11 +20,15 @@ const defaultGitConfig = Object.freeze({
|
||||
class DummyGitConfigService implements GitConfigService {
|
||||
constructor(private config: { [index: string]: string | undefined }) {}
|
||||
|
||||
async get<T extends string>(key: string): Promise<T | null> {
|
||||
return (this.config[key] || null) as T | null;
|
||||
async get<T extends string>(key: string): Promise<T | undefined> {
|
||||
return (this.config[key] || undefined) as T | undefined;
|
||||
}
|
||||
|
||||
async set<T extends string>(key: string, value: T): Promise<T | null> {
|
||||
async getWithDefault<T extends string>(key: string): Promise<T> {
|
||||
return this.config[key] as T;
|
||||
}
|
||||
|
||||
async set<T extends string>(key: string, value: T): Promise<T | undefined> {
|
||||
return (this.config[key] = value);
|
||||
}
|
||||
}
|
||||
@ -42,14 +46,14 @@ class DummyAIClient implements AIClient {
|
||||
|
||||
const examplePatch = `
|
||||
@@ -52,7 +52,8 @@
|
||||
|
||||
|
||||
export enum AnthropicModelName {
|
||||
Opus = 'claude-3-opus-20240229',
|
||||
- Sonnet = 'claude-3-sonnet-20240229'
|
||||
+ Sonnet = 'claude-3-sonnet-20240229',
|
||||
+ Haiku = 'claude-3-haiku-20240307'
|
||||
}
|
||||
|
||||
|
||||
export const AI_SERVICE_CONTEXT = Symbol();
|
||||
`;
|
||||
|
||||
|
@ -56,6 +56,30 @@ export enum AnthropicModelName {
|
||||
Haiku = 'claude-3-haiku-20240307'
|
||||
}
|
||||
|
||||
export enum ConfigKeys {
|
||||
ModelProvider = 'gitbutler.aiModelProvider',
|
||||
OpenAIKeyOption = 'gitbutler.aiOpenAIKeyOption',
|
||||
OpenAIModelName = 'gitbutler.aiOpenAIModelName',
|
||||
OpenAIKey = 'gitbutler.aiOpenAIKey',
|
||||
AnthropicKeyOption = 'gitbutler.aiAnthropicKeyOption',
|
||||
AnthropicModelName = 'gitbutler.aiAnthropicModelName',
|
||||
AnthropicKey = 'gitbutler.aiAnthropicKey'
|
||||
}
|
||||
|
||||
type SummarizeCommitOpts = {
|
||||
diff: string;
|
||||
useEmojiStyle?: boolean;
|
||||
useBriefStyle?: boolean;
|
||||
commitTemplate?: string;
|
||||
userToken?: string;
|
||||
};
|
||||
|
||||
type SummarizeBranchOpts = {
|
||||
diff: string;
|
||||
branchTemplate?: string;
|
||||
userToken?: string;
|
||||
};
|
||||
|
||||
export class AIService {
|
||||
constructor(
|
||||
private gitConfig: GitConfigService,
|
||||
@ -66,13 +90,18 @@ export class AIService {
|
||||
// Firstly, if the user has opted to use the GB API and isn't logged in, it will return undefined
|
||||
// Secondly, if the user has opted to bring their own key but hasn't provided one, it will return undefined
|
||||
async buildClient(userToken?: string): Promise<undefined | AIClient> {
|
||||
const modelKind =
|
||||
(await this.gitConfig.get<ModelKind>('gitbutler.aiModelProvider')) || ModelKind.OpenAI;
|
||||
const openAIKeyOption =
|
||||
(await this.gitConfig.get<KeyOption>('gitbutler.aiOpenAIKeyOption')) || KeyOption.ButlerAPI;
|
||||
const anthropicKeyOption =
|
||||
(await this.gitConfig.get<KeyOption>('gitbutler.aiAnthropicKeyOption')) ||
|
||||
KeyOption.ButlerAPI;
|
||||
const modelKind = await this.gitConfig.getWithDefault<ModelKind>(
|
||||
ConfigKeys.ModelProvider,
|
||||
ModelKind.OpenAI
|
||||
);
|
||||
const openAIKeyOption = await this.gitConfig.getWithDefault<KeyOption>(
|
||||
ConfigKeys.OpenAIKeyOption,
|
||||
KeyOption.ButlerAPI
|
||||
);
|
||||
const anthropicKeyOption = await this.gitConfig.getWithDefault<KeyOption>(
|
||||
ConfigKeys.AnthropicKeyOption,
|
||||
KeyOption.ButlerAPI
|
||||
);
|
||||
|
||||
if (
|
||||
(modelKind == ModelKind.OpenAI && openAIKeyOption == KeyOption.ButlerAPI) ||
|
||||
@ -80,24 +109,22 @@ export class AIService {
|
||||
) {
|
||||
if (!userToken) {
|
||||
toasts.error("When using GitButler's API to summarize code, you must be logged in");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
return new ButlerAIClient(this.cloud, userToken, ModelKind.OpenAI);
|
||||
}
|
||||
|
||||
if (modelKind == ModelKind.OpenAI) {
|
||||
const openAIModelName =
|
||||
(await this.gitConfig.get<OpenAIModelName>('gitbutler.aiOpenAIModelName')) ||
|
||||
OpenAIModelName.GPT35Turbo;
|
||||
const openAIKey = await this.gitConfig.get('gitbutler.aiOpenAIKey');
|
||||
const openAIModelName = await this.gitConfig.getWithDefault<OpenAIModelName>(
|
||||
ConfigKeys.OpenAIModelName,
|
||||
OpenAIModelName.GPT35Turbo
|
||||
);
|
||||
const openAIKey = await this.gitConfig.get(ConfigKeys.OpenAIKey);
|
||||
|
||||
if (!openAIKey) {
|
||||
toasts.error(
|
||||
'When using OpenAI in a bring your own key configuration, you must provide a valid token'
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -105,17 +132,16 @@ export class AIService {
|
||||
return new OpenAIClient(openAIModelName, openAI);
|
||||
}
|
||||
if (modelKind == ModelKind.Anthropic) {
|
||||
const anthropicModelName =
|
||||
(await this.gitConfig.get<AnthropicModelName>('gitbutler.aiAnthropicModelName')) ||
|
||||
AnthropicModelName.Haiku;
|
||||
const anthropicKey = await this.gitConfig.get('gitbutler.aiAnthropicKey');
|
||||
const anthropicModelName = await this.gitConfig.getWithDefault<AnthropicModelName>(
|
||||
ConfigKeys.AnthropicModelName,
|
||||
AnthropicModelName.Haiku
|
||||
);
|
||||
const anthropicKey = await this.gitConfig.get(ConfigKeys.AnthropicKey);
|
||||
|
||||
// TODO: Provide feedback to user
|
||||
if (!anthropicKey) {
|
||||
toasts.error(
|
||||
'When using Anthropic in a bring your own key configuration, you must provide a valid token'
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -129,31 +155,21 @@ export class AIService {
|
||||
useBriefStyle = false,
|
||||
commitTemplate = defaultCommitTemplate,
|
||||
userToken
|
||||
}: {
|
||||
diff: string;
|
||||
useEmojiStyle?: boolean;
|
||||
useBriefStyle?: boolean;
|
||||
commitTemplate?: string;
|
||||
userToken?: string;
|
||||
}) {
|
||||
}: SummarizeCommitOpts) {
|
||||
const aiClient = await this.buildClient(userToken);
|
||||
if (!aiClient) return;
|
||||
|
||||
let prompt = commitTemplate.replaceAll('%{diff}', diff.slice(0, diffLengthLimit));
|
||||
|
||||
if (useBriefStyle) {
|
||||
prompt = prompt.replaceAll(
|
||||
'%{brief_style}',
|
||||
'The commit message must be only one sentence and as short as possible.'
|
||||
);
|
||||
} else {
|
||||
prompt = prompt.replaceAll('%{brief_style}', '');
|
||||
}
|
||||
if (useEmojiStyle) {
|
||||
prompt = prompt.replaceAll('%{emoji_style}', 'Make use of GitMoji in the title prefix.');
|
||||
} else {
|
||||
prompt = prompt.replaceAll('%{emoji_style}', "Don't use any emoji.");
|
||||
}
|
||||
const briefPart = useBriefStyle
|
||||
? 'The commit message must be only one sentence and as short as possible.'
|
||||
: '';
|
||||
prompt = prompt.replaceAll('%{brief_style}', briefPart);
|
||||
|
||||
const emojiPart = useEmojiStyle
|
||||
? 'Make use of GitMoji in the title prefix.'
|
||||
: "Don't use any emoji.";
|
||||
prompt = prompt.replaceAll('%{emoji_style}', emojiPart);
|
||||
|
||||
let message = await aiClient.evaluate(prompt);
|
||||
|
||||
@ -161,9 +177,8 @@ export class AIService {
|
||||
message = message.split('\n')[0];
|
||||
}
|
||||
|
||||
const firstNewLine = message.indexOf('\n');
|
||||
const summary = firstNewLine > -1 ? message.slice(0, firstNewLine).trim() : message;
|
||||
const description = firstNewLine > -1 ? message.slice(firstNewLine + 1).trim() : '';
|
||||
const parts = message.split(/\n+(.*?)\w*/s);
|
||||
const [summary, description] = [parts[0] || '', parts[1] || ''];
|
||||
|
||||
return description.length > 0 ? `${summary}\n\n${description}` : summary;
|
||||
}
|
||||
@ -172,20 +187,12 @@ export class AIService {
|
||||
diff,
|
||||
branchTemplate = defaultBranchTemplate,
|
||||
userToken = undefined
|
||||
}: {
|
||||
diff: string;
|
||||
branchTemplate?: string;
|
||||
userToken?: string;
|
||||
}) {
|
||||
}: SummarizeBranchOpts) {
|
||||
const aiClient = await this.buildClient(userToken);
|
||||
if (!aiClient) return;
|
||||
|
||||
const prompt = branchTemplate.replaceAll('%{diff}', diff.slice(0, diffLengthLimit));
|
||||
|
||||
let message = await aiClient.evaluate(prompt);
|
||||
|
||||
message = message.replaceAll(' ', '-');
|
||||
message = message.replaceAll('\n', '-');
|
||||
return message;
|
||||
const message = await aiClient.evaluate(prompt);
|
||||
return message.replaceAll(' ', '-').replaceAll('\n', '-');
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,16 @@
|
||||
import { invoke } from '@tauri-apps/api/tauri';
|
||||
|
||||
export class GitConfigService {
|
||||
get<T extends string>(key: string): Promise<T | null> {
|
||||
return invoke<T | null>('git_get_global_config', { key });
|
||||
async get<T extends string>(key: string): Promise<T | undefined> {
|
||||
return await invoke<T | undefined>('git_get_global_config', { key });
|
||||
}
|
||||
|
||||
set<T extends string>(key: string, value: T) {
|
||||
return invoke<T | null>('git_set_global_config', { key, value });
|
||||
async getWithDefault<T extends string>(key: string, defaultValue: T): Promise<T> {
|
||||
const value = await invoke<T | undefined>('git_get_global_config', { key });
|
||||
return value || defaultValue;
|
||||
}
|
||||
|
||||
async set<T extends string>(key: string, value: T) {
|
||||
return invoke<T | undefined>('git_set_global_config', { key, value });
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
import TextBox from './TextBox.svelte';
|
||||
import {
|
||||
AnthropicModelName,
|
||||
ConfigKeys,
|
||||
KeyOption,
|
||||
ModelKind,
|
||||
OpenAIModelName
|
||||
@ -17,36 +18,48 @@
|
||||
const gitConfigService = getContextByClass(GitConfigService);
|
||||
|
||||
let modelKind: ModelKind;
|
||||
$: gitConfigService.set('gitbutler.aiModelProvider', modelKind);
|
||||
let openAIKeyOption: KeyOption;
|
||||
$: gitConfigService.set('gitbutler.aiOpenAIKeyOption', openAIKeyOption);
|
||||
let anthropicKeyOption: KeyOption;
|
||||
$: gitConfigService.set('gitbutler.aiAnthropicKeyOption', anthropicKeyOption);
|
||||
let openAIKey: string | undefined;
|
||||
$: if (openAIKey) gitConfigService.set('gitbutler.aiOpenAIKey', openAIKey);
|
||||
let openAIModelName: OpenAIModelName;
|
||||
$: gitConfigService.set('gitbutler.aiOpenAIModelName', openAIModelName);
|
||||
let anthropicKey: string | undefined;
|
||||
$: if (anthropicKey) gitConfigService.set('gitbutler.aiAnthropicKey', anthropicKey);
|
||||
let anthropicModelName: AnthropicModelName;
|
||||
|
||||
$: gitConfigService.set('gitbutler.aiModelProvider', modelKind);
|
||||
|
||||
$: gitConfigService.set('gitbutler.aiOpenAIKeyOption', openAIKeyOption);
|
||||
$: gitConfigService.set('gitbutler.aiOpenAIModelName', openAIModelName);
|
||||
$: if (openAIKey) gitConfigService.set('gitbutler.aiOpenAIKey', openAIKey);
|
||||
|
||||
$: gitConfigService.set('gitbutler.aiAnthropicKeyOption', anthropicKeyOption);
|
||||
$: gitConfigService.set('gitbutler.aiAnthropicModelName', anthropicModelName);
|
||||
$: if (anthropicKey) gitConfigService.set('gitbutler.aiAnthropicKey', anthropicKey);
|
||||
|
||||
onMount(async () => {
|
||||
modelKind =
|
||||
(await gitConfigService.get<ModelKind>('gitbutler.aiModelProvider')) || ModelKind.OpenAI;
|
||||
openAIKeyOption =
|
||||
(await gitConfigService.get<KeyOption>('gitbutler.aiOpenAIKeyOption')) || KeyOption.ButlerAPI;
|
||||
anthropicKeyOption =
|
||||
(await gitConfigService.get<KeyOption>('gitbutler.aiAnthropicKeyOption')) ||
|
||||
KeyOption.ButlerAPI;
|
||||
openAIModelName =
|
||||
(await gitConfigService.get<OpenAIModelName>('gitbutler.aiOpenAIModelName')) ||
|
||||
OpenAIModelName.GPT35Turbo;
|
||||
openAIKey = (await gitConfigService.get('gitbutler.aiOpenAIKey')) || undefined;
|
||||
anthropicModelName =
|
||||
(await gitConfigService.get<AnthropicModelName>('gitbutler.aiAnthropicModelName')) ||
|
||||
AnthropicModelName.Haiku;
|
||||
anthropicKey = (await gitConfigService.get('gitbutler.aiAnthropicKey')) || undefined;
|
||||
modelKind = await gitConfigService.getWithDefault<ModelKind>(
|
||||
ConfigKeys.ModelProvider,
|
||||
ModelKind.OpenAI
|
||||
);
|
||||
|
||||
openAIKeyOption = await gitConfigService.getWithDefault<KeyOption>(
|
||||
ConfigKeys.OpenAIKeyOption,
|
||||
KeyOption.ButlerAPI
|
||||
);
|
||||
openAIModelName = await gitConfigService.getWithDefault<OpenAIModelName>(
|
||||
ConfigKeys.OpenAIModelName,
|
||||
OpenAIModelName.GPT35Turbo
|
||||
);
|
||||
openAIKey = await gitConfigService.get(ConfigKeys.OpenAIKey);
|
||||
|
||||
anthropicKeyOption = await gitConfigService.getWithDefault<KeyOption>(
|
||||
ConfigKeys.AnthropicKeyOption,
|
||||
KeyOption.ButlerAPI
|
||||
);
|
||||
anthropicModelName = await gitConfigService.getWithDefault<AnthropicModelName>(
|
||||
ConfigKeys.AnthropicModelName,
|
||||
AnthropicModelName.Haiku
|
||||
);
|
||||
anthropicKey = await gitConfigService.get(ConfigKeys.AnthropicKey);
|
||||
});
|
||||
|
||||
$: if (form) form.modelKind.value = modelKind;
|
||||
|
Loading…
Reference in New Issue
Block a user