Merge pull request #14646 from Budibase/ai-configs-design-updates
Ai configs design updates
This commit is contained in:
commit
9aa196effe
|
@ -4,6 +4,7 @@
|
|||
import OpenAILogo from "./logos/OpenAI.svelte"
|
||||
import AnthropicLogo from "./logos/Anthropic.svelte"
|
||||
import TogetherAILogo from "./logos/TogetherAI.svelte"
|
||||
import AzureOpenAILogo from "./logos/AzureOpenAI.svelte"
|
||||
import { Providers } from "./constants"
|
||||
|
||||
const logos = {
|
||||
|
@ -11,6 +12,7 @@
|
|||
[Providers.OpenAI.name]: OpenAILogo,
|
||||
[Providers.Anthropic.name]: AnthropicLogo,
|
||||
[Providers.TogetherAI.name]: TogetherAILogo,
|
||||
[Providers.AzureOpenAI.name]: AzureOpenAILogo,
|
||||
}
|
||||
|
||||
export let config
|
||||
|
@ -26,8 +28,8 @@
|
|||
<div class="icon">
|
||||
<svelte:component
|
||||
this={logos[config.name || config.provider]}
|
||||
height="30"
|
||||
width="30"
|
||||
height="18"
|
||||
width="18"
|
||||
/>
|
||||
</div>
|
||||
<div class="header">
|
||||
|
@ -110,7 +112,7 @@
|
|||
|
||||
.tag {
|
||||
display: flex;
|
||||
color: var(--spectrum-body-m-text-color);
|
||||
color: #ffffff;
|
||||
padding: 4px 8px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { it, expect, describe, vi } from "vitest"
|
||||
import AISettings from "./index.svelte"
|
||||
import { render } from "@testing-library/svelte"
|
||||
import { render, fireEvent } from "@testing-library/svelte"
|
||||
import { admin, licensing } from "stores/portal"
|
||||
import { notifications } from "@budibase/bbui"
|
||||
|
||||
|
@ -55,39 +55,43 @@ describe("AISettings", () => {
|
|||
expect(enterpriseTag).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it("should show the premium label on cloud when Budibase AI isn't enabled", async () => {
|
||||
setupEnv(Hosting.Cloud)
|
||||
instance = render(AISettings, {})
|
||||
const premiumTag = instance.queryByText("Premium")
|
||||
expect(premiumTag).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it("should not show the add configuration button if the user doesn't have the correct license on cloud", async () => {
|
||||
it("the add configuration button should not do anything the user doesn't have the correct license on cloud", async () => {
|
||||
let addConfigurationButton
|
||||
let configModal
|
||||
|
||||
setupEnv(Hosting.Cloud)
|
||||
instance = render(AISettings)
|
||||
addConfigurationButton = instance.queryByText("Add configuration")
|
||||
expect(addConfigurationButton).not.toBeInTheDocument()
|
||||
expect(addConfigurationButton).toBeInTheDocument()
|
||||
await fireEvent.click(addConfigurationButton)
|
||||
configModal = instance.queryByText("Custom AI Configuration")
|
||||
expect(configModal).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it("the add configuration button should open the config modal if the user has the correct license on cloud", async () => {
|
||||
let addConfigurationButton
|
||||
let configModal
|
||||
|
||||
setupEnv(Hosting.Cloud, { customAIConfigsEnabled: true })
|
||||
instance = render(AISettings)
|
||||
addConfigurationButton = instance.queryByText("Add configuration")
|
||||
expect(addConfigurationButton).toBeInTheDocument()
|
||||
await fireEvent.click(addConfigurationButton)
|
||||
configModal = instance.queryByText("Custom AI Configuration")
|
||||
expect(configModal).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it("should not show the add configuration button if the user doesn't have the correct license on self host", async () => {
|
||||
it("the add configuration button should open the config modal if the user has the correct license on self host", async () => {
|
||||
let addConfigurationButton
|
||||
|
||||
setupEnv(Hosting.Self)
|
||||
instance = render(AISettings)
|
||||
addConfigurationButton = instance.queryByText("Add configuration")
|
||||
expect(addConfigurationButton).not.toBeInTheDocument()
|
||||
let configModal
|
||||
|
||||
setupEnv(Hosting.Self, { customAIConfigsEnabled: true })
|
||||
instance = render(AISettings, {})
|
||||
instance = render(AISettings)
|
||||
addConfigurationButton = instance.queryByText("Add configuration")
|
||||
expect(addConfigurationButton).toBeInTheDocument()
|
||||
await fireEvent.click(addConfigurationButton)
|
||||
configModal = instance.queryByText("Custom AI Configuration")
|
||||
expect(configModal).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -84,8 +84,10 @@
|
|||
<Label size="M">API Key</Label>
|
||||
<Input type="password" bind:value={config.apiKey} />
|
||||
</div>
|
||||
<Toggle text="Active" bind:value={config.active} />
|
||||
<Toggle text="Set as default" bind:value={config.isDefault} />
|
||||
<div class="form-row">
|
||||
<Toggle text="Active" bind:value={config.active} />
|
||||
<Toggle text="Set as default" bind:value={config.isDefault} />
|
||||
</div>
|
||||
</ModalContent>
|
||||
|
||||
<style>
|
||||
|
|
|
@ -23,7 +23,7 @@ export const Providers = {
|
|||
models: [{ label: "Llama 3 8B", value: "meta-llama/Meta-Llama-3-8B" }],
|
||||
},
|
||||
AzureOpenAI: {
|
||||
name: "Azure Open AI",
|
||||
name: "Azure OpenAI",
|
||||
models: [
|
||||
{ label: "GPT 4o Mini", value: "gpt-4o-mini" },
|
||||
{ label: "GPT 4o", value: "gpt-4o" },
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
let editingUuid
|
||||
|
||||
$: isCloud = $admin.cloud
|
||||
$: budibaseAIEnabled = $licensing.budibaseAIEnabled
|
||||
$: customAIConfigsEnabled = $licensing.customAIConfigsEnabled
|
||||
|
||||
async function fetchAIConfig() {
|
||||
|
@ -127,18 +126,8 @@
|
|||
</Modal>
|
||||
<Layout noPadding>
|
||||
<Layout gap="XS" noPadding>
|
||||
<Heading size="M">AI</Heading>
|
||||
{#if isCloud && !budibaseAIEnabled}
|
||||
<Tags>
|
||||
<Tag icon="LockClosed">Premium</Tag>
|
||||
</Tags>
|
||||
{/if}
|
||||
<Body>Configure your AI settings within this section:</Body>
|
||||
</Layout>
|
||||
<Divider />
|
||||
<Layout noPadding>
|
||||
<div class="config-heading">
|
||||
<Heading size="S">AI Configurations</Heading>
|
||||
<div class="header">
|
||||
<Heading size="M">AI</Heading>
|
||||
{#if !isCloud && !customAIConfigsEnabled}
|
||||
<Tags>
|
||||
<Tag icon="LockClosed">Premium</Tag>
|
||||
|
@ -147,24 +136,43 @@
|
|||
<Tags>
|
||||
<Tag icon="LockClosed">Enterprise</Tag>
|
||||
</Tags>
|
||||
{:else}
|
||||
<Button size="S" cta on:click={newConfig}>Add configuration</Button>
|
||||
{/if}
|
||||
</div>
|
||||
<Body size="S"
|
||||
>Use the following interface to select your preferred AI configuration.</Body
|
||||
>
|
||||
<Body size="S">Select your AI Model:</Body>
|
||||
{#if fullAIConfig?.config}
|
||||
{#each Object.keys(fullAIConfig.config) as key}
|
||||
<AIConfigTile
|
||||
config={fullAIConfig.config[key]}
|
||||
editHandler={() => editConfig(key)}
|
||||
deleteHandler={() => deleteConfig(key)}
|
||||
/>
|
||||
{/each}
|
||||
{/if}
|
||||
<Body>Configure your AI settings within this section:</Body>
|
||||
</Layout>
|
||||
<Divider />
|
||||
<div style={`opacity: ${customAIConfigsEnabled ? 1 : 0.5}`}>
|
||||
<Layout noPadding>
|
||||
<div class="config-heading">
|
||||
<Heading size="S">AI Configurations</Heading>
|
||||
<Button
|
||||
size="S"
|
||||
cta={customAIConfigsEnabled}
|
||||
secondary={!customAIConfigsEnabled}
|
||||
on:click={customAIConfigsEnabled ? newConfig : null}
|
||||
>
|
||||
Add configuration
|
||||
</Button>
|
||||
</div>
|
||||
<Body size="S"
|
||||
>Use the following interface to select your preferred AI configuration.</Body
|
||||
>
|
||||
{#if customAIConfigsEnabled}
|
||||
<Body size="S">Select your AI Model:</Body>
|
||||
{/if}
|
||||
{#if fullAIConfig?.config}
|
||||
{#each Object.keys(fullAIConfig.config) as key}
|
||||
<AIConfigTile
|
||||
config={fullAIConfig.config[key]}
|
||||
editHandler={customAIConfigsEnabled ? () => editConfig(key) : null}
|
||||
deleteHandler={customAIConfigsEnabled
|
||||
? () => deleteConfig(key)
|
||||
: null}
|
||||
/>
|
||||
{/each}
|
||||
{/if}
|
||||
</Layout>
|
||||
</div>
|
||||
</Layout>
|
||||
|
||||
<style>
|
||||
|
@ -172,5 +180,12 @@
|
|||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: -18px;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
<script>
|
||||
export let width
|
||||
export let height
|
||||
</script>
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" {width} {height} viewBox="0 0 96 96">
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="e399c19f-b68f-429d-b176-18c2117ff73c"
|
||||
x1="-1032.172"
|
||||
x2="-1059.213"
|
||||
y1="145.312"
|
||||
y2="65.426"
|
||||
gradientTransform="matrix(1 0 0 -1 1075 158)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop offset="0" stop-color="#114a8b" />
|
||||
<stop offset="1" stop-color="#0669bc" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="ac2a6fc2-ca48-4327-9a3c-d4dcc3256e15"
|
||||
x1="-1023.725"
|
||||
x2="-1029.98"
|
||||
y1="108.083"
|
||||
y2="105.968"
|
||||
gradientTransform="matrix(1 0 0 -1 1075 158)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop offset="0" stop-opacity=".3" />
|
||||
<stop offset=".071" stop-opacity=".2" />
|
||||
<stop offset=".321" stop-opacity=".1" />
|
||||
<stop offset=".623" stop-opacity=".05" />
|
||||
<stop offset="1" stop-opacity="0" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="a7fee970-a784-4bb1-af8d-63d18e5f7db9"
|
||||
x1="-1027.165"
|
||||
x2="-997.482"
|
||||
y1="147.642"
|
||||
y2="68.561"
|
||||
gradientTransform="matrix(1 0 0 -1 1075 158)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop offset="0" stop-color="#3ccbf4" />
|
||||
<stop offset="1" stop-color="#2892df" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<path
|
||||
fill="url(#e399c19f-b68f-429d-b176-18c2117ff73c)"
|
||||
d="M33.338 6.544h26.038l-27.03 80.087a4.152 4.152 0 0 1-3.933 2.824H8.149a4.145 4.145 0 0 1-3.928-5.47L29.404 9.368a4.152 4.152 0 0 1 3.934-2.825z"
|
||||
/>
|
||||
<path
|
||||
fill="#0078d4"
|
||||
d="M71.175 60.261h-41.29a1.911 1.911 0 0 0-1.305 3.309l26.532 24.764a4.171 4.171 0 0 0 2.846 1.121h23.38z"
|
||||
/>
|
||||
<path
|
||||
fill="url(#ac2a6fc2-ca48-4327-9a3c-d4dcc3256e15)"
|
||||
d="M33.338 6.544a4.118 4.118 0 0 0-3.943 2.879L4.252 83.917a4.14 4.14 0 0 0 3.908 5.538h20.787a4.443 4.443 0 0 0 3.41-2.9l5.014-14.777 17.91 16.705a4.237 4.237 0 0 0 2.666.972H81.24L71.024 60.261l-29.781.007L59.47 6.544z"
|
||||
/>
|
||||
<path
|
||||
fill="url(#a7fee970-a784-4bb1-af8d-63d18e5f7db9)"
|
||||
d="M66.595 9.364a4.145 4.145 0 0 0-3.928-2.82H33.648a4.146 4.146 0 0 1 3.928 2.82l25.184 74.62a4.146 4.146 0 0 1-3.928 5.472h29.02a4.146 4.146 0 0 0 3.927-5.472z"
|
||||
/>
|
||||
</svg>
|
|
@ -1,5 +1,10 @@
|
|||
<script>
|
||||
import { redirect } from "@roxi/routify"
|
||||
import { licensing } from "stores/portal"
|
||||
|
||||
$redirect("./auth")
|
||||
if ($licensing.customAIConfigsEnabled) {
|
||||
$redirect("./ai")
|
||||
} else {
|
||||
$redirect("./auth")
|
||||
}
|
||||
</script>
|
||||
|
|
Loading…
Reference in New Issue