Allow AI use without user and update settings to match

This commit is contained in:
Caleb Owens 2024-03-18 09:37:33 +00:00 committed by Mattias Granlund
parent fcd4e616ac
commit ef8734d638
7 changed files with 136 additions and 67 deletions

View File

@ -89,6 +89,35 @@ export class AIService {
private cloud: ReturnType<typeof getCloudApiClient>
) {}
async configurationValid(userToken?: string) {
const modelKind = await this.gitConfig.getWithDefault<ModelKind>(
GitAIConfigKey.ModelProvider,
ModelKind.OpenAI
);
const openAIKeyOption = await this.gitConfig.getWithDefault<KeyOption>(
GitAIConfigKey.OpenAIKeyOption,
KeyOption.ButlerAPI
);
const openAIKey = await this.gitConfig.get(GitAIConfigKey.OpenAIKey);
const anthropicKeyOption = await this.gitConfig.getWithDefault<KeyOption>(
GitAIConfigKey.AnthropicKeyOption,
KeyOption.ButlerAPI
);
const anthropicKey = await this.gitConfig.get(GitAIConfigKey.AnthropicKey);
if (
(modelKind == ModelKind.OpenAI && openAIKeyOption == KeyOption.ButlerAPI) ||
(modelKind == ModelKind.Anthropic && anthropicKeyOption == KeyOption.ButlerAPI)
) {
return Boolean(userToken);
}
return Boolean(
(modelKind == ModelKind.OpenAI && openAIKey) ||
(modelKind == ModelKind.Anthropic && anthropicKey)
);
}
// This optionally returns a summarizer. There are a few conditions for how this may occur
// 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

View File

@ -2,6 +2,7 @@
import Select from './Select.svelte';
import SelectItem from './SelectItem.svelte';
import TextBox from './TextBox.svelte';
import WelcomeSigninAction from './WelcomeSigninAction.svelte';
import {
AnthropicModelName,
GitAIConfigKey,
@ -12,10 +13,13 @@
import { GitConfigService } from '$lib/backend/gitConfigService';
import RadioButton from '$lib/components/RadioButton.svelte';
import SectionCard from '$lib/components/SectionCard.svelte';
import { UserService } from '$lib/stores/user';
import { getContextByClass } from '$lib/utils/context';
import { onMount } from 'svelte';
const gitConfigService = getContextByClass(GitConfigService);
const userService = getContextByClass(UserService);
const user = userService.user;
let modelKind: ModelKind;
let openAIKeyOption: KeyOption;
@ -166,6 +170,8 @@
{item.name}
</SelectItem>
</Select>
{:else if !$user}
<WelcomeSigninAction prompt="A user is required to make use of the GitButler API" />
{/if}
</div>
</SectionCard>
@ -219,6 +225,8 @@
{item.name}
</SelectItem>
</Select>
{:else if !$user}
<WelcomeSigninAction prompt="A user is required to make use of the GitButler API" />
{/if}
</div>
</SectionCard>

View File

@ -113,7 +113,7 @@
generateBranchName();
}
onMount(() => {
onMount(async () => {
laneWidth = lscache.get(laneWidthKey + branch.id);
});

View File

@ -1,4 +1,5 @@
<script lang="ts">
import { AIService } from '$lib/backend/aiService';
import Button from '$lib/components/Button.svelte';
import Modal from '$lib/components/Modal.svelte';
import TextBox from '$lib/components/TextBox.svelte';
@ -6,10 +7,12 @@
import ContextMenuItem from '$lib/components/contextmenu/ContextMenuItem.svelte';
import ContextMenuSection from '$lib/components/contextmenu/ContextMenuSection.svelte';
import { projectAiGenEnabled } from '$lib/config/config';
import { UserService } from '$lib/stores/user';
import { normalizeBranchName } from '$lib/utils/branch';
import { getContextByClass } from '$lib/utils/context';
import { BranchController } from '$lib/vbranches/branchController';
import { createEventDispatcher } from 'svelte';
import type { User } from '$lib/backend/cloud';
import type { Branch } from '$lib/vbranches/types';
export let branch: Branch;
@ -18,6 +21,9 @@
export let isUnapplied = false;
const branchController = getContextByClass(BranchController);
const aiService = getContextByClass(AIService);
const userService = getContextByClass(UserService);
const user = userService.user;
let deleteBranchModal: Modal;
let renameRemoteModal: Modal;
@ -32,6 +38,14 @@
$: commits = branch.commits;
$: hasIntegratedCommits =
commits.length > 0 ? commits.some((c) => c.status == 'integrated') : false;
let aiConfigurationValid = false;
$: setAIConfigurationValid($user);
async function setAIConfigurationValid(user: User | undefined) {
aiConfigurationValid = await aiService.configurationValid(user?.access_token);
}
</script>
{#if visible}
@ -69,7 +83,10 @@
dispatch('action', 'generate-branch-name');
visible = false;
}}
disabled={isUnapplied || !$aiGenEnabled || branch.files?.length == 0 || !branch.active}
disabled={isUnapplied ||
!($aiGenEnabled && aiConfigurationValid) ||
branch.files?.length == 0 ||
!branch.active}
/>
</ContextMenuSection>
<ContextMenuSection>

View File

@ -1,10 +1,10 @@
<script lang="ts">
import SectionCard from './SectionCard.svelte';
import WelcomeSigninAction from './WelcomeSigninAction.svelte';
import { getCloudApiClient } from '$lib/backend/cloud';
import Link from '$lib/components/Link.svelte';
import Spacer from '$lib/components/Spacer.svelte';
import Toggle from '$lib/components/Toggle.svelte';
import WelcomeSigninAction from '$lib/components/WelcomeSigninAction.svelte';
import { projectAiGenEnabled } from '$lib/config/config';
import { projectAiGenAutoBranchNamingEnabled } from '$lib/config/config';
import { UserService } from '$lib/stores/user';
@ -62,69 +62,77 @@
}
</script>
{#if $user}
<div class="aigen-wrap">
<SectionCard labelFor="aiGenEnabled" on:click={aiGenToggle} orientation="row">
<svelte:fragment slot="title">Enable branch and commit message generation</svelte:fragment>
<svelte:fragment slot="caption">
Uses OpenAI's API. If enabled, diffs will sent to OpenAI's servers when pressing the
"Generate message" button.
</svelte:fragment>
<svelte:fragment slot="actions">
<Toggle id="aiGenEnabled" checked={$aiGenEnabled} on:change={aiGenToggle} />
</svelte:fragment>
</SectionCard>
<SectionCard
labelFor="branchNameGen"
disabled={!$aiGenEnabled}
on:click={aiGenBranchNamesToggle}
orientation="row"
>
<svelte:fragment slot="title">Automatically generate branch names</svelte:fragment>
<svelte:fragment slot="actions">
<Toggle
id="branchNameGen"
disabled={!$aiGenEnabled}
checked={$aiGenAutoBranchNamingEnabled}
on:change={aiGenBranchNamesToggle}
/>
</svelte:fragment>
</SectionCard>
</div>
{#if !$user}
<WelcomeSigninAction />
<Spacer />
{/if}
{#if $user.role === 'admin'}
<h3 class="text-base-15 text-bold">Full data synchronization</h3>
<div class="aigen-wrap">
<h3 class="text-base-15 text-bold">AI Options</h3>
<p class="text-base-body-12">
GitButler supports the use of OpenAI and Anthropic to provide commit message and branch name
generation. This works either through GitButler's API or in a bring your own key configuration
and can be configured in the main preferences screen.
</p>
<SectionCard labelFor="historySync" on:change={(e) => onSyncChange(e.detail)} orientation="row">
<svelte:fragment slot="caption">
Sync my history, repository and branch data for backup, sharing and team features.
</svelte:fragment>
<svelte:fragment slot="actions">
<Toggle
id="historySync"
checked={project.api?.sync || false}
on:change={(e) => onSyncChange(e.detail)}
/>
</svelte:fragment>
</SectionCard>
<SectionCard labelFor="aiGenEnabled" on:click={aiGenToggle} orientation="row">
<svelte:fragment slot="title">Enable branch and commit message generation</svelte:fragment>
<svelte:fragment slot="caption">
If enabled, diffs will sent to OpenAI or Anthropic's servers when pressing the "Generate
message" and "Generate branch name" button.
</svelte:fragment>
<svelte:fragment slot="actions">
<Toggle id="aiGenEnabled" checked={$aiGenEnabled} on:change={aiGenToggle} />
</svelte:fragment>
</SectionCard>
{#if project.api}
<div class="api-link">
<Link
target="_blank"
rel="noreferrer"
href="{PUBLIC_API_BASE_URL}projects/{project.api?.repository_id}"
>Go to GitButler Cloud Project</Link
>
</div>
{/if}
<Spacer />
<SectionCard
labelFor="branchNameGen"
disabled={!$aiGenEnabled}
on:click={aiGenBranchNamesToggle}
orientation="row"
>
<svelte:fragment slot="title">Automatically generate branch names</svelte:fragment>
<svelte:fragment slot="actions">
<Toggle
id="branchNameGen"
disabled={!$aiGenEnabled}
checked={$aiGenAutoBranchNamingEnabled}
on:change={aiGenBranchNamesToggle}
/>
</svelte:fragment>
</SectionCard>
</div>
<Spacer />
{#if $user?.role === 'admin'}
<h3 class="text-base-15 text-bold">Full data synchronization</h3>
<SectionCard labelFor="historySync" on:change={(e) => onSyncChange(e.detail)} orientation="row">
<svelte:fragment slot="caption">
Sync my history, repository and branch data for backup, sharing and team features.
</svelte:fragment>
<svelte:fragment slot="actions">
<Toggle
id="historySync"
checked={project.api?.sync || false}
on:change={(e) => onSyncChange(e.detail)}
/>
</svelte:fragment>
</SectionCard>
{#if project.api}
<div class="api-link">
<Link
target="_blank"
rel="noreferrer"
href="{PUBLIC_API_BASE_URL}projects/{project.api?.repository_id}"
>Go to GitButler Cloud Project</Link
>
</div>
{/if}
{:else}
<WelcomeSigninAction />
<Spacer />
{/if}

View File

@ -20,7 +20,7 @@
import { setAutoHeight } from '$lib/utils/useAutoHeight';
import { useResize } from '$lib/utils/useResize';
import { BranchController } from '$lib/vbranches/branchController';
import { createEventDispatcher } from 'svelte';
import { createEventDispatcher, onMount } from 'svelte';
import { quintOut } from 'svelte/easing';
import { fly, slide } from 'svelte/transition';
import type { User } from '$lib/backend/cloud';
@ -135,6 +135,12 @@
descriptionTextArea.focus();
}, 0);
}
let aiConfigurationValid = false;
onMount(async () => {
aiConfigurationValid = await aiService.configurationValid(user?.access_token);
});
</script>
<div class="commit-box" class:commit-box__expanded={$expanded}>
@ -212,15 +218,15 @@
<div
class="commit-box__texarea-actions"
use:tooltip={$aiGenEnabled && user
use:tooltip={$aiGenEnabled && aiConfigurationValid
? ''
: 'You must be logged in and have summary generation enabled to use this feature'}
: 'You must be logged in or have provided your own API key and have summary generation enabled to use this feature'}
>
<DropDownButton
kind="outlined"
icon="ai-small"
color="neutral"
disabled={!$aiGenEnabled || !user}
disabled={!($aiGenEnabled && aiConfigurationValid)}
loading={aiLoading}
on:click={() => generateCommitMessage(branch.files)}
>

View File

@ -4,6 +4,9 @@
import { UserService } from '$lib/stores/user';
import { getContextByClass } from '$lib/utils/context';
export let prompt: string =
'Enable GitButler features like automatic branch and commit message generation.';
const userService = getContextByClass(UserService);
const user = userService.user;
@ -35,8 +38,6 @@
<svelte:fragment slot="icon">
{@html signinSvg}
</svelte:fragment>
<svelte:fragment slot="message">
Enable GitButler features like automatic branch and commit message generation.
</svelte:fragment>
<svelte:fragment slot="message">{prompt}</svelte:fragment>
</WelcomeAction>
{/if}