initial impl of default Budibase AI config through env vars

This commit is contained in:
Martin McKeaveney 2024-09-03 19:47:36 +01:00
parent be4b6aaceb
commit d3b90a0253
6 changed files with 47 additions and 20 deletions

View File

@ -1,5 +1,6 @@
<script> <script>
import { Body, Label, Icon } from "@budibase/bbui" import { Body, Label, Icon } from "@budibase/bbui"
import BudibaseLogo from "./logos/Budibase.svelte"
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"
@ -7,6 +8,7 @@
export let config export let config
export let disabled export let disabled
export let budibaseAIDefault
export let editHandler export let editHandler
export let deleteHandler export let deleteHandler
@ -16,7 +18,9 @@
<!-- svelte-ignore a11y-click-events-have-key-events --> <!-- svelte-ignore a11y-click-events-have-key-events -->
<div on:click class:disabled class="option"> <div on:click class:disabled class="option">
<div class="icon"> <div class="icon">
{#if config.provider === Providers.OpenAI.name} {#if config.name === "Budibase AI"}
<BudibaseLogo height="30" width="30"/>
{:else if config.provider === Providers.OpenAI.name}
<OpenAILogo height="30" width="30"/> <OpenAILogo height="30" width="30"/>
{:else if config.provider === Providers.Anthropic.name} {:else if config.provider === Providers.Anthropic.name}
<AnthropicLogo height="30" width="30"/> <AnthropicLogo height="30" width="30"/>

View File

@ -19,7 +19,7 @@
let validation let validation
$: { $: {
const {provider, defaultModel, name, apiKey} = config const { provider, defaultModel, name, apiKey } = config
validation = provider && defaultModel && name && apiKey validation = provider && defaultModel && name && apiKey
} }
$: canEditBaseUrl = config.provider && ConfigMap[config.provider].baseUrl === "" $: canEditBaseUrl = config.provider && ConfigMap[config.provider].baseUrl === ""
@ -49,10 +49,6 @@
title="Custom AI Configuration" title="Custom AI Configuration"
> >
<div class="form-row"> <div class="form-row">
<div class="form-row">
<Label size="M">Name</Label>
<Input placeholder={"Test 1"} bind:value={config.name}/>
</div>
<Label size="M">Provider</Label> <Label size="M">Provider</Label>
<Select <Select
placeholder={null} placeholder={null}
@ -61,6 +57,10 @@
on:change={prefillConfig} on:change={prefillConfig}
/> />
</div> </div>
<div class="form-row">
<Label size="M">Name</Label>
<Input placeholder={"Enter a name"} bind:value={config.name}/>
</div>
<div class="form-row"> <div class="form-row">
<Label size="M">Default Model</Label> <Label size="M">Default Model</Label>
{#if config.provider !== Providers.Custom.name} {#if config.provider !== Providers.Custom.name}

View File

@ -33,24 +33,31 @@ export const Providers = {
}, },
AzureOpenAI: { AzureOpenAI: {
name: "Azure Open AI", name: "Azure Open AI",
models: ["whatever"] models: [
"gpt-4o-mini",
]
}, },
Custom: { Custom: {
name: "Custom", name: "Custom",
// TODO: too many - probably need to use an autocomplete for this
models: [""]
}, },
} }
export const ConfigMap = { export const ConfigMap = {
OpenAI: { OpenAI: {
baseUrl: "https://api.openai.com" name: "OpenAI",
baseUrl: "https://api.openai.com",
}, },
Anthropic: { Anthropic: {
baseUrl: "" name: "Anthropic",
baseUrl: "",
}, },
TogetherAI: { TogetherAI: {
baseUrl: "https://api.together.xyz/v1" name: "TogetherAI",
baseUrl: "https://api.together.xyz/v1",
},
AzureOpenAI: {
name: "Azure OpenAI",
baseUrl: "",
}, },
Custom: { Custom: {
baseUrl: "" baseUrl: ""

View File

@ -111,16 +111,16 @@ export interface SCIMInnerConfig {
export interface SCIMConfig extends Config<SCIMInnerConfig> {} export interface SCIMConfig extends Config<SCIMInnerConfig> {}
type AIProvider = "OpenAI" | "Anthropic" | "AzureOpenAI" | "Custom"
export interface AIInnerConfig { export interface AIInnerConfig {
[key: string]: { [key: string]: {
// TODO: should be enum provider: AIProvider
provider: string
isDefault: boolean isDefault: boolean
name: string name: string
active: boolean active: boolean
baseUrl: string baseUrl?: string
apiKey: string apiKey?: string
// TODO: should be enum
defaultModel: string defaultModel: string
} }
} }

View File

@ -320,12 +320,26 @@ function enrichOIDCLogos(oidcLogos: OIDCLogosConfig) {
) )
} }
function sanitizeAIConfig(aiConfig: AIConfig) { async function enrichAIConfig(aiConfig: AIConfig) {
// Strip out the API Keys from the response so they don't show in the UI
for (let key in aiConfig.config) { for (let key in aiConfig.config) {
if (aiConfig.config[key].apiKey) { if (aiConfig.config[key].apiKey) {
aiConfig.config[key].apiKey = PASSWORD_REPLACEMENT aiConfig.config[key].apiKey = PASSWORD_REPLACEMENT
} }
} }
// Return the Budibase AI data source as part of the response if licensing allows
const budibaseAIEnabled = await pro.features.isBudibaseAIEnabled()
if (budibaseAIEnabled) {
aiConfig.config["budibase_ai"] = {
provider: "OpenAI",
active: true,
isDefault: true,
defaultModel: env.BUDIBASE_AI_DEFAULT_MODEL,
name: "Budibase AI",
}
}
return aiConfig return aiConfig
} }
@ -341,8 +355,7 @@ export async function find(ctx: UserCtx) {
} }
if (type === ConfigType.AI) { if (type === ConfigType.AI) {
// TODO: do the licensing checks here and return the right things based on the license await enrichAIConfig(scopedConfig)
sanitizeAIConfig(scopedConfig)
} }
ctx.body = scopedConfig ctx.body = scopedConfig
} else { } else {

View File

@ -70,6 +70,9 @@ const environment = {
PASSPORT_OIDCAUTH_FAILURE_REDIRECT: PASSPORT_OIDCAUTH_FAILURE_REDIRECT:
process.env.PASSPORT_OIDCAUTH_FAILURE_REDIRECT || "/error", process.env.PASSPORT_OIDCAUTH_FAILURE_REDIRECT || "/error",
// Budibase AI
BUDIBASE_AI_API_KEY: process.env.BUDIBASE_AI_API_KEY,
BUDIBASE_AI_DEFAULT_MODEL: process.env.BUDIBASE_AI_DEFAULT_MODEL,
_set(key: any, value: any) { _set(key: any, value: any) {
process.env[key] = value process.env[key] = value
// @ts-ignore // @ts-ignore