Merge branch 'master' into BUDI-8565/related-columns
This commit is contained in:
commit
747057b011
|
@ -4,6 +4,7 @@
|
||||||
import OpenAILogo from "./logos/OpenAI.svelte"
|
import OpenAILogo from "./logos/OpenAI.svelte"
|
||||||
import AnthropicLogo from "./logos/Anthropic.svelte"
|
import AnthropicLogo from "./logos/Anthropic.svelte"
|
||||||
import TogetherAILogo from "./logos/TogetherAI.svelte"
|
import TogetherAILogo from "./logos/TogetherAI.svelte"
|
||||||
|
import AzureOpenAILogo from "./logos/AzureOpenAI.svelte"
|
||||||
import { Providers } from "./constants"
|
import { Providers } from "./constants"
|
||||||
|
|
||||||
const logos = {
|
const logos = {
|
||||||
|
@ -11,6 +12,7 @@
|
||||||
[Providers.OpenAI.name]: OpenAILogo,
|
[Providers.OpenAI.name]: OpenAILogo,
|
||||||
[Providers.Anthropic.name]: AnthropicLogo,
|
[Providers.Anthropic.name]: AnthropicLogo,
|
||||||
[Providers.TogetherAI.name]: TogetherAILogo,
|
[Providers.TogetherAI.name]: TogetherAILogo,
|
||||||
|
[Providers.AzureOpenAI.name]: AzureOpenAILogo,
|
||||||
}
|
}
|
||||||
|
|
||||||
export let config
|
export let config
|
||||||
|
@ -26,8 +28,8 @@
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
<svelte:component
|
<svelte:component
|
||||||
this={logos[config.name || config.provider]}
|
this={logos[config.name || config.provider]}
|
||||||
height="30"
|
height="18"
|
||||||
width="30"
|
width="18"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="header">
|
<div class="header">
|
||||||
|
@ -110,7 +112,7 @@
|
||||||
|
|
||||||
.tag {
|
.tag {
|
||||||
display: flex;
|
display: flex;
|
||||||
color: var(--spectrum-body-m-text-color);
|
color: #ffffff;
|
||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { it, expect, describe, vi } from "vitest"
|
import { it, expect, describe, vi } from "vitest"
|
||||||
import AISettings from "./index.svelte"
|
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 { admin, licensing } from "stores/portal"
|
||||||
import { notifications } from "@budibase/bbui"
|
import { notifications } from "@budibase/bbui"
|
||||||
|
|
||||||
|
@ -55,39 +55,43 @@ describe("AISettings", () => {
|
||||||
expect(enterpriseTag).toBeInTheDocument()
|
expect(enterpriseTag).toBeInTheDocument()
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should show the premium label on cloud when Budibase AI isn't enabled", async () => {
|
it("the add configuration button should not do anything the user doesn't have the correct license on cloud", 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 () => {
|
|
||||||
let addConfigurationButton
|
let addConfigurationButton
|
||||||
|
let configModal
|
||||||
|
|
||||||
setupEnv(Hosting.Cloud)
|
setupEnv(Hosting.Cloud)
|
||||||
instance = render(AISettings)
|
instance = render(AISettings)
|
||||||
addConfigurationButton = instance.queryByText("Add configuration")
|
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 })
|
setupEnv(Hosting.Cloud, { customAIConfigsEnabled: true })
|
||||||
instance = render(AISettings)
|
instance = render(AISettings)
|
||||||
addConfigurationButton = instance.queryByText("Add configuration")
|
addConfigurationButton = instance.queryByText("Add configuration")
|
||||||
expect(addConfigurationButton).toBeInTheDocument()
|
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
|
let addConfigurationButton
|
||||||
|
let configModal
|
||||||
setupEnv(Hosting.Self)
|
|
||||||
instance = render(AISettings)
|
|
||||||
addConfigurationButton = instance.queryByText("Add configuration")
|
|
||||||
expect(addConfigurationButton).not.toBeInTheDocument()
|
|
||||||
|
|
||||||
setupEnv(Hosting.Self, { customAIConfigsEnabled: true })
|
setupEnv(Hosting.Self, { customAIConfigsEnabled: true })
|
||||||
instance = render(AISettings, {})
|
instance = render(AISettings)
|
||||||
addConfigurationButton = instance.queryByText("Add configuration")
|
addConfigurationButton = instance.queryByText("Add configuration")
|
||||||
expect(addConfigurationButton).toBeInTheDocument()
|
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>
|
<Label size="M">API Key</Label>
|
||||||
<Input type="password" bind:value={config.apiKey} />
|
<Input type="password" bind:value={config.apiKey} />
|
||||||
</div>
|
</div>
|
||||||
<Toggle text="Active" bind:value={config.active} />
|
<div class="form-row">
|
||||||
<Toggle text="Set as default" bind:value={config.isDefault} />
|
<Toggle text="Active" bind:value={config.active} />
|
||||||
|
<Toggle text="Set as default" bind:value={config.isDefault} />
|
||||||
|
</div>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|
|
@ -23,7 +23,7 @@ export const Providers = {
|
||||||
models: [{ label: "Llama 3 8B", value: "meta-llama/Meta-Llama-3-8B" }],
|
models: [{ label: "Llama 3 8B", value: "meta-llama/Meta-Llama-3-8B" }],
|
||||||
},
|
},
|
||||||
AzureOpenAI: {
|
AzureOpenAI: {
|
||||||
name: "Azure Open AI",
|
name: "Azure OpenAI",
|
||||||
models: [
|
models: [
|
||||||
{ label: "GPT 4o Mini", value: "gpt-4o-mini" },
|
{ label: "GPT 4o Mini", value: "gpt-4o-mini" },
|
||||||
{ label: "GPT 4o", value: "gpt-4o" },
|
{ label: "GPT 4o", value: "gpt-4o" },
|
||||||
|
|
|
@ -27,7 +27,6 @@
|
||||||
let editingUuid
|
let editingUuid
|
||||||
|
|
||||||
$: isCloud = $admin.cloud
|
$: isCloud = $admin.cloud
|
||||||
$: budibaseAIEnabled = $licensing.budibaseAIEnabled
|
|
||||||
$: customAIConfigsEnabled = $licensing.customAIConfigsEnabled
|
$: customAIConfigsEnabled = $licensing.customAIConfigsEnabled
|
||||||
|
|
||||||
async function fetchAIConfig() {
|
async function fetchAIConfig() {
|
||||||
|
@ -127,18 +126,8 @@
|
||||||
</Modal>
|
</Modal>
|
||||||
<Layout noPadding>
|
<Layout noPadding>
|
||||||
<Layout gap="XS" noPadding>
|
<Layout gap="XS" noPadding>
|
||||||
<Heading size="M">AI</Heading>
|
<div class="header">
|
||||||
{#if isCloud && !budibaseAIEnabled}
|
<Heading size="M">AI</Heading>
|
||||||
<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>
|
|
||||||
{#if !isCloud && !customAIConfigsEnabled}
|
{#if !isCloud && !customAIConfigsEnabled}
|
||||||
<Tags>
|
<Tags>
|
||||||
<Tag icon="LockClosed">Premium</Tag>
|
<Tag icon="LockClosed">Premium</Tag>
|
||||||
|
@ -147,24 +136,43 @@
|
||||||
<Tags>
|
<Tags>
|
||||||
<Tag icon="LockClosed">Enterprise</Tag>
|
<Tag icon="LockClosed">Enterprise</Tag>
|
||||||
</Tags>
|
</Tags>
|
||||||
{:else}
|
|
||||||
<Button size="S" cta on:click={newConfig}>Add configuration</Button>
|
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<Body size="S"
|
<Body>Configure your AI settings within this section:</Body>
|
||||||
>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}
|
|
||||||
</Layout>
|
</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>
|
</Layout>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@ -172,5 +180,12 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
margin-bottom: -18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
}
|
}
|
||||||
</style>
|
</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>
|
<script>
|
||||||
import { redirect } from "@roxi/routify"
|
import { redirect } from "@roxi/routify"
|
||||||
|
import { licensing } from "stores/portal"
|
||||||
|
|
||||||
$redirect("./auth")
|
if ($licensing.customAIConfigsEnabled) {
|
||||||
|
$redirect("./ai")
|
||||||
|
} else {
|
||||||
|
$redirect("./auth")
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
Loading…
Reference in New Issue