diff --git a/packages/account-portal b/packages/account-portal
index c24374879d..ce60bf9231 160000
--- a/packages/account-portal
+++ b/packages/account-portal
@@ -1 +1 @@
-Subproject commit c24374879d2b61516fabc24d7404e7da235be05e
+Subproject commit ce60bf923115855a9cd0bb991297cccd308eae13
diff --git a/packages/backend-core/src/events/publishers/ai.ts b/packages/backend-core/src/events/publishers/ai.ts
new file mode 100644
index 0000000000..168ac9f796
--- /dev/null
+++ b/packages/backend-core/src/events/publishers/ai.ts
@@ -0,0 +1,21 @@
+import { publishEvent } from "../events"
+import {
+ Event,
+ AIConfigCreatedEvent,
+ AIConfigUpdatedEvent,
+} from "@budibase/types"
+
+async function AIConfigCreated(timestamp?: string | number) {
+ const properties: AIConfigCreatedEvent = {}
+ await publishEvent(Event.AI_CONFIG_CREATED, properties, timestamp)
+}
+
+async function AIConfigUpdated() {
+ const properties: AIConfigUpdatedEvent = {}
+ await publishEvent(Event.AI_CONFIG_UPDATED, properties)
+}
+
+export default {
+ AIConfigCreated,
+ AIConfigUpdated,
+}
diff --git a/packages/backend-core/src/events/publishers/index.ts b/packages/backend-core/src/events/publishers/index.ts
index 87a34bf3f1..9c92b80499 100644
--- a/packages/backend-core/src/events/publishers/index.ts
+++ b/packages/backend-core/src/events/publishers/index.ts
@@ -4,6 +4,7 @@ export { default as auth } from "./auth"
export { default as automation } from "./automation"
export { default as datasource } from "./datasource"
export { default as email } from "./email"
+export { default as ai } from "./ai"
export { default as license } from "./license"
export { default as layout } from "./layout"
export { default as org } from "./org"
diff --git a/packages/builder/src/pages/builder/portal/settings/ai/ConfigModal.svelte b/packages/builder/src/pages/builder/portal/settings/ai/ConfigModal.svelte
index 2f7486d087..d480689028 100644
--- a/packages/builder/src/pages/builder/portal/settings/ai/ConfigModal.svelte
+++ b/packages/builder/src/pages/builder/portal/settings/ai/ConfigModal.svelte
@@ -54,7 +54,11 @@
-
+
diff --git a/packages/pro b/packages/pro
index a4d1d15d9c..62586bc47b 160000
--- a/packages/pro
+++ b/packages/pro
@@ -1 +1 @@
-Subproject commit a4d1d15d9ce6ac3deedb2e42625c90ba32756758
+Subproject commit 62586bc47bf2af507356b0e1e64df8b0ae9fad43
diff --git a/packages/types/src/documents/global/quotas.ts b/packages/types/src/documents/global/quotas.ts
index 4eb1168f7d..4726dabf0d 100644
--- a/packages/types/src/documents/global/quotas.ts
+++ b/packages/types/src/documents/global/quotas.ts
@@ -35,6 +35,7 @@ export interface StaticUsage {
[StaticQuotaName.CREATORS]: number
[StaticQuotaName.USER_GROUPS]: number
[StaticQuotaName.ROWS]: number
+ [StaticQuotaName.AI_CUSTOM_CONFIGS]: number
triggers: {
[key in StaticQuotaName]?: QuotaTriggers
}
@@ -44,6 +45,7 @@ export interface MonthlyUsage {
[MonthlyQuotaName.QUERIES]: number
[MonthlyQuotaName.AUTOMATIONS]: number
[MonthlyQuotaName.DAY_PASSES]: number
+ [MonthlyQuotaName.BUDIBASE_AI_CREDITS]: number
triggers: {
[key in MonthlyQuotaName]?: QuotaTriggers
}
diff --git a/packages/types/src/sdk/events/ai.ts b/packages/types/src/sdk/events/ai.ts
new file mode 100644
index 0000000000..740c59d5f9
--- /dev/null
+++ b/packages/types/src/sdk/events/ai.ts
@@ -0,0 +1,5 @@
+import { BaseEvent } from "./event"
+
+export interface AIConfigCreatedEvent extends BaseEvent {}
+
+export interface AIConfigUpdatedEvent extends BaseEvent {}
diff --git a/packages/types/src/sdk/events/event.ts b/packages/types/src/sdk/events/event.ts
index 05f4fe4be9..242b182dec 100644
--- a/packages/types/src/sdk/events/event.ts
+++ b/packages/types/src/sdk/events/event.ts
@@ -33,6 +33,10 @@ export enum Event {
EMAIL_SMTP_CREATED = "email:smtp:created",
EMAIL_SMTP_UPDATED = "email:smtp:updated",
+ // AI
+ AI_CONFIG_CREATED = "ai:config:created",
+ AI_CONFIG_UPDATED = "ai:config:updated",
+
// AUTH
AUTH_SSO_CREATED = "auth:sso:created",
AUTH_SSO_UPDATED = "auth:sso:updated",
@@ -243,6 +247,10 @@ export const AuditedEventFriendlyName: Record = {
[Event.EMAIL_SMTP_CREATED]: `Email configuration created`,
[Event.EMAIL_SMTP_UPDATED]: `Email configuration updated`,
+ // AI
+ [Event.AI_CONFIG_CREATED]: `AI configuration created`,
+ [Event.AI_CONFIG_UPDATED]: `AI configuration updated`,
+
// AUTH
[Event.AUTH_SSO_CREATED]: `SSO configuration created`,
[Event.AUTH_SSO_UPDATED]: `SSO configuration updated`,
diff --git a/packages/types/src/sdk/events/index.ts b/packages/types/src/sdk/events/index.ts
index 745f84d2a3..043e62faa4 100644
--- a/packages/types/src/sdk/events/index.ts
+++ b/packages/types/src/sdk/events/index.ts
@@ -2,6 +2,7 @@ export * from "./app"
export * from "./auth"
export * from "./automation"
export * from "./email"
+export * from "./ai"
export * from "./datasource"
export * from "./event"
export * from "./layout"
diff --git a/packages/worker/src/api/controllers/global/configs.ts b/packages/worker/src/api/controllers/global/configs.ts
index d3eef71240..b8feb6158f 100644
--- a/packages/worker/src/api/controllers/global/configs.ts
+++ b/packages/worker/src/api/controllers/global/configs.ts
@@ -31,6 +31,7 @@ import {
OIDCLogosConfig,
AIConfig,
PASSWORD_REPLACEMENT,
+ isAIConfig,
} from "@budibase/types"
import * as pro from "@budibase/pro"
@@ -40,6 +41,11 @@ const getEventFns = async (config: Config, existing?: Config) => {
if (!existing) {
if (isSMTPConfig(config)) {
fns.push(events.email.SMTPCreated)
+ } else if (isAIConfig(config)) {
+ fns.push(() => events.ai.AIConfigCreated)
+ fns.push(() =>
+ pro.quotas.updateCustomAIConfigCount(Object.keys(config.config).length)
+ )
} else if (isGoogleConfig(config)) {
fns.push(() => events.auth.SSOCreated(ConfigType.GOOGLE))
if (config.config.activated) {
@@ -76,6 +82,11 @@ const getEventFns = async (config: Config, existing?: Config) => {
} else {
if (isSMTPConfig(config)) {
fns.push(events.email.SMTPUpdated)
+ } else if (isAIConfig(config)) {
+ fns.push(() => events.ai.AIConfigUpdated)
+ fns.push(() =>
+ pro.quotas.updateCustomAIConfigCount(Object.keys(config.config).length)
+ )
} else if (isGoogleConfig(config)) {
fns.push(() => events.auth.SSOUpdated(ConfigType.GOOGLE))
if (!existing.config.activated && config.config.activated) {