From cec819abff29b085d80e50cf3f1d6d813126979f Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Tue, 15 Apr 2025 08:34:25 +0100 Subject: [PATCH 01/12] fix line height issue --- .../components/common/CodeEditor/AIGen.svelte | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/packages/builder/src/components/common/CodeEditor/AIGen.svelte b/packages/builder/src/components/common/CodeEditor/AIGen.svelte index 019407b7b5..4bd4765dc5 100644 --- a/packages/builder/src/components/common/CodeEditor/AIGen.svelte +++ b/packages/builder/src/components/common/CodeEditor/AIGen.svelte @@ -22,20 +22,27 @@ let previousContents: string | null = null let expanded = false let containerWidth = "auto" - let containerHeight = "40px" + let containerHeight = "44px" let promptText = "" let animateBorder = false - function adjustContainerHeight() { if (promptInput && buttonElement) { + if (!promptText.trim()) { + promptInput.style.height = "24px" + containerHeight = "44px" + return + } + promptInput.style.height = "0px" const newHeight = Math.min(promptInput.scrollHeight, 100) promptInput.style.height = `${newHeight}px` - containerHeight = `${Math.max(40, newHeight + 20)}px` + containerHeight = `${Math.max(44, newHeight + 18)}px` + } else if (!promptInput) { + containerHeight = "44px" } } - $: if (promptInput && promptText) adjustContainerHeight() + $: if (promptInput) adjustContainerHeight() async function generateJs(prompt: string) { if (!prompt.trim()) return @@ -76,7 +83,7 @@ function resetExpand() { expanded = false containerWidth = "auto" - containerHeight = "40px" + containerHeight = "44px" promptText = "" suggestedCode = null previousContents = null @@ -91,7 +98,7 @@ containerWidth = parentWidth ? `${Math.min(Math.max(parentWidth * 0.8, 300), 600)}px` : "300px" - containerHeight = "40px" + containerHeight = "44px" setTimeout(() => { promptInput?.focus() }, 250) @@ -190,7 +197,7 @@ From d39f41decbad6c442db186ebf751a706c870d879 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Tue, 15 Apr 2025 11:39:54 +0100 Subject: [PATCH 04/12] further ux updates --- .../components/common/CodeEditor/AIGen.svelte | 86 ++++++++++--------- 1 file changed, 46 insertions(+), 40 deletions(-) diff --git a/packages/builder/src/components/common/CodeEditor/AIGen.svelte b/packages/builder/src/components/common/CodeEditor/AIGen.svelte index c1435abd3a..3b1e8ffff2 100644 --- a/packages/builder/src/components/common/CodeEditor/AIGen.svelte +++ b/packages/builder/src/components/common/CodeEditor/AIGen.svelte @@ -6,7 +6,11 @@ Button, Modal, ModalContent, + Body, + Link, } from "@budibase/bbui" + import { auth, admin } from "@/stores/portal" + import { createEventDispatcher } from "svelte" import { API } from "@/api" import type { EnrichedBinding } from "@budibase/types" @@ -30,12 +34,13 @@ let containerWidth = "auto" let promptText = "" let animateBorder = false - let aiEnabled = true - let hasAICredits = true - let showSwitchOnAIModal = false - let showAddCreditsModal = false - let switchOnAIModal: Modal | null = null - let addCreditsModal: Modal | null = null + let aiEnabled = false + let creditsExceeded = false // TODO: Make this computed when quota is implemented + let switchOnAIModal: Modal + let addCreditsModal: Modal + + $: accountPortalAccess = $auth?.user?.accountPortalAccess + $: accountPortal = $admin.accountPortalUrl async function generateJs(prompt: string) { if (!prompt.trim()) return @@ -86,7 +91,7 @@ if (!expanded) { expanded = true animateBorder = true - // Calculate width based on size of CodeEditor parent + // Calculate width based on size of parent containerWidth = parentWidth ? `${Math.min(Math.max(parentWidth * 0.8, 300), 600)}px` : "300px" @@ -114,6 +119,8 @@ } + +
{#if suggestedCode !== null}
@@ -137,7 +144,10 @@ src={BBAI} alt="AI" class="ai-icon" - class:disabled={(!aiEnabled || !hasAICredits) && expanded} + on:click={e => { + e.stopPropagation() + toggleExpand() + }} /> {#if expanded} {:else} @@ -159,29 +169,40 @@ {#if expanded}
{#if !aiEnabled} - - (showSwitchOnAIModal = false)} - > - Enable AI features for your workspace. + +
+

To enable BB AI:

+
    +
  • + Add your Budibase license key: + Budibase account portal +
  • +
  • + Go to the portal settings page, click AI and switch on BB AI +
  • +
+
- {:else if !hasAICredits} - - (showAddCreditsModal = false)} - > - Purchase more AI credits to continue using AI features. + + + {#if accountPortalAccess} + Contact sales to unlock additional BB AI credits + {:else} + Contact your account holder to unlock additional BB AI credits + {/if} + {:else} @@ -196,16 +217,6 @@ on:click={() => generateJs(promptText)} /> {/if} - { - e.stopPropagation() - if (!suggestedCode && !promptLoading) toggleExpand() - }} - />
{/if} @@ -334,6 +345,7 @@ height: 18px; margin-right: 8px; flex-shrink: 0; + cursor: pointer; } .ai-gen-text { @@ -384,10 +396,4 @@ color: var(--spectrum-global-color-gray-500); cursor: not-allowed; } - - .ai-icon.disabled { - filter: grayscale(1) brightness(1.5); - opacity: 0.5; - cursor: not-allowed; - } From a93596e28e24fd0ca2c75e65c4887e11abc6507b Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Tue, 15 Apr 2025 11:49:02 +0100 Subject: [PATCH 05/12] proper handling of ai being enabled --- .../src/components/common/CodeEditor/AIGen.svelte | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/builder/src/components/common/CodeEditor/AIGen.svelte b/packages/builder/src/components/common/CodeEditor/AIGen.svelte index 3b1e8ffff2..53ae78e76f 100644 --- a/packages/builder/src/components/common/CodeEditor/AIGen.svelte +++ b/packages/builder/src/components/common/CodeEditor/AIGen.svelte @@ -24,7 +24,7 @@ accept: void reject: { code: string | null } }>() - + $: console.log($auth.user?.llm) let promptInput: HTMLInputElement let buttonElement: HTMLButtonElement let promptLoading = false @@ -34,13 +34,13 @@ let containerWidth = "auto" let promptText = "" let animateBorder = false - let aiEnabled = false let creditsExceeded = false // TODO: Make this computed when quota is implemented let switchOnAIModal: Modal let addCreditsModal: Modal $: accountPortalAccess = $auth?.user?.accountPortalAccess $: accountPortal = $admin.accountPortalUrl + $: aiEnabled = !!$auth?.user?.llm async function generateJs(prompt: string) { if (!prompt.trim()) return @@ -144,6 +144,8 @@ src={BBAI} alt="AI" class="ai-icon" + class:disabled={expanded && + (suggestedCode !== null || !aiEnabled || creditsExceeded)} on:click={e => { e.stopPropagation() toggleExpand() @@ -378,6 +380,7 @@ gap: var(--spacing-s); z-index: 4; flex-shrink: 0; + margin-right: var(--spacing-s); } .button-content-wrapper { @@ -396,4 +399,9 @@ color: var(--spectrum-global-color-gray-500); cursor: not-allowed; } + + .ai-icon.disabled { + filter: grayscale(1) brightness(1.5); + opacity: 0.5; + } From 94cf6200b89dd1d3bf71228261396b7ea0f2e36c Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Tue, 15 Apr 2025 11:55:11 +0100 Subject: [PATCH 06/12] support an expandedOnly state --- .../components/common/CodeEditor/AIGen.svelte | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/packages/builder/src/components/common/CodeEditor/AIGen.svelte b/packages/builder/src/components/common/CodeEditor/AIGen.svelte index 53ae78e76f..b17be8a86a 100644 --- a/packages/builder/src/components/common/CodeEditor/AIGen.svelte +++ b/packages/builder/src/components/common/CodeEditor/AIGen.svelte @@ -37,10 +37,12 @@ let creditsExceeded = false // TODO: Make this computed when quota is implemented let switchOnAIModal: Modal let addCreditsModal: Modal + export let expandedOnly: boolean = false $: accountPortalAccess = $auth?.user?.accountPortalAccess $: accountPortal = $admin.accountPortalUrl $: aiEnabled = !!$auth?.user?.llm + $: expanded = expandedOnly ? true : expanded async function generateJs(prompt: string) { if (!prompt.trim()) return @@ -146,10 +148,12 @@ class="ai-icon" class:disabled={expanded && (suggestedCode !== null || !aiEnabled || creditsExceeded)} - on:click={e => { - e.stopPropagation() - toggleExpand() - }} + on:click={!expandedOnly + ? e => { + e.stopPropagation() + toggleExpand() + } + : undefined} /> {#if expanded} From c6493f2f998318b143076400a5cde131289f3e4d Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Tue, 15 Apr 2025 11:58:06 +0100 Subject: [PATCH 07/12] missing prop --- .../builder/src/components/common/CodeEditor/AIGen.svelte | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/builder/src/components/common/CodeEditor/AIGen.svelte b/packages/builder/src/components/common/CodeEditor/AIGen.svelte index b17be8a86a..6ffe197640 100644 --- a/packages/builder/src/components/common/CodeEditor/AIGen.svelte +++ b/packages/builder/src/components/common/CodeEditor/AIGen.svelte @@ -18,13 +18,15 @@ export let bindings: EnrichedBinding[] = [] export let value: string | null = "" + export let expandedOnly: boolean = false + export let parentWidth: number | null = null export const dispatch = createEventDispatcher<{ update: { code: string } accept: void reject: { code: string | null } }>() - $: console.log($auth.user?.llm) + let promptInput: HTMLInputElement let buttonElement: HTMLButtonElement let promptLoading = false @@ -37,7 +39,6 @@ let creditsExceeded = false // TODO: Make this computed when quota is implemented let switchOnAIModal: Modal let addCreditsModal: Modal - export let expandedOnly: boolean = false $: accountPortalAccess = $auth?.user?.accountPortalAccess $: accountPortal = $admin.accountPortalUrl From 0709f68f47c2fa39acc36f3957ea9b62be3caca2 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Tue, 15 Apr 2025 13:11:16 +0100 Subject: [PATCH 08/12] pass correct prop --- .../components/common/CodeEditor/AIGen.svelte | 16 +++++++++++----- .../common/CodeEditor/CodeEditor.svelte | 1 + 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/packages/builder/src/components/common/CodeEditor/AIGen.svelte b/packages/builder/src/components/common/CodeEditor/AIGen.svelte index 6ffe197640..d68b2ee496 100644 --- a/packages/builder/src/components/common/CodeEditor/AIGen.svelte +++ b/packages/builder/src/components/common/CodeEditor/AIGen.svelte @@ -42,8 +42,11 @@ $: accountPortalAccess = $auth?.user?.accountPortalAccess $: accountPortal = $admin.accountPortalUrl - $: aiEnabled = !!$auth?.user?.llm + $: aiEnabled = false $: expanded = expandedOnly ? true : expanded + $: if (expandedOnly) { + containerWidth = calculateExpandedWidth() + } async function generateJs(prompt: string) { if (!prompt.trim()) return @@ -90,14 +93,17 @@ animateBorder = false } + function calculateExpandedWidth() { + return parentWidth + ? `${Math.min(Math.max(parentWidth * 0.8, 300), 600)}px` + : "300px" + } + function toggleExpand() { if (!expanded) { expanded = true animateBorder = true - // Calculate width based on size of parent - containerWidth = parentWidth - ? `${Math.min(Math.max(parentWidth * 0.8, 300), 600)}px` - : "300px" + containerWidth = calculateExpandedWidth() setTimeout(() => { promptInput?.focus() }, 250) diff --git a/packages/builder/src/components/common/CodeEditor/CodeEditor.svelte b/packages/builder/src/components/common/CodeEditor/CodeEditor.svelte index c0bbe192bc..0448d4d048 100644 --- a/packages/builder/src/components/common/CodeEditor/CodeEditor.svelte +++ b/packages/builder/src/components/common/CodeEditor/CodeEditor.svelte @@ -469,6 +469,7 @@ {#if aiGenEnabled} Date: Tue, 15 Apr 2025 13:45:22 +0100 Subject: [PATCH 09/12] pr comments --- .../components/common/CodeEditor/AIGen.svelte | 12 +++++----- .../builder/src/stores/portal/licensing.ts | 22 +++++++++++++++++++ 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/packages/builder/src/components/common/CodeEditor/AIGen.svelte b/packages/builder/src/components/common/CodeEditor/AIGen.svelte index d68b2ee496..f9bd665640 100644 --- a/packages/builder/src/components/common/CodeEditor/AIGen.svelte +++ b/packages/builder/src/components/common/CodeEditor/AIGen.svelte @@ -9,7 +9,7 @@ Body, Link, } from "@budibase/bbui" - import { auth, admin } from "@/stores/portal" + import { auth, admin, licensing } from "@/stores/portal" import { createEventDispatcher } from "svelte" import { API } from "@/api" @@ -36,14 +36,15 @@ let containerWidth = "auto" let promptText = "" let animateBorder = false - let creditsExceeded = false // TODO: Make this computed when quota is implemented let switchOnAIModal: Modal let addCreditsModal: Modal $: accountPortalAccess = $auth?.user?.accountPortalAccess $: accountPortal = $admin.accountPortalUrl - $: aiEnabled = false + $: aiEnabled = $auth?.user?.llm $: expanded = expandedOnly ? true : expanded + $: creditsExceeded = $licensing.aiCreditsExceeded + $: disabled = suggestedCode !== null || !aiEnabled || creditsExceeded $: if (expandedOnly) { containerWidth = calculateExpandedWidth() } @@ -153,8 +154,7 @@ src={BBAI} alt="AI" class="ai-icon" - class:disabled={expanded && - (suggestedCode !== null || !aiEnabled || creditsExceeded)} + class:disabled={expanded && disabled} on:click={!expandedOnly ? e => { e.stopPropagation() @@ -170,7 +170,7 @@ class="prompt-input" placeholder="Generate Javascript..." on:keydown={handleKeyPress} - disabled={suggestedCode !== null || !aiEnabled || creditsExceeded} + {disabled} readonly={suggestedCode !== null} /> {:else} diff --git a/packages/builder/src/stores/portal/licensing.ts b/packages/builder/src/stores/portal/licensing.ts index bb28a56d91..c995b83469 100644 --- a/packages/builder/src/stores/portal/licensing.ts +++ b/packages/builder/src/stores/portal/licensing.ts @@ -56,7 +56,9 @@ interface LicensingState { // user limits userCount?: number userLimit?: number + aiCreditsLimit?: number userLimitReached: boolean + aiCreditsExceeded: boolean errUserLimit: boolean } @@ -102,6 +104,8 @@ class LicensingStore extends BudiStore { userLimit: undefined, userLimitReached: false, errUserLimit: false, + // AI Limits + aiCreditsExceeded: false, }) } @@ -119,6 +123,16 @@ class LicensingStore extends BudiStore { return userCount > userLimit } + aiCreditsExceeded( + aiCredits: number, + aiCreditsLimit = get(this.store).aiCreditsLimit + ) { + if (aiCreditsLimit === UNLIMITED || aiCreditsLimit === undefined) { + return false + } + return aiCredits > aiCreditsLimit + } + async isCloud() { let adminStore = get(admin) if (!adminStore.loaded) { @@ -291,9 +305,15 @@ class LicensingStore extends BudiStore { const userQuota = license.quotas.usage.static.users const userLimit = userQuota.value + const aiCreditsQuota = license.quotas.usage.monthly.budibaseAICredits + const aiCreditsLimit = aiCreditsQuota.value const userCount = usage.usageQuota.users const userLimitReached = this.usersLimitReached(userCount, userLimit) const userLimitExceeded = this.usersLimitExceeded(userCount, userLimit) + const aiCreditsExceeded = this.aiCreditsExceeded( + usage.monthly.current.budibaseAICredits, + aiCreditsLimit + ) const isCloudAccount = await this.isCloud() const errUserLimit = isCloudAccount && @@ -315,6 +335,8 @@ class LicensingStore extends BudiStore { userLimit, userLimitReached, errUserLimit, + aiCreditsLimit, + aiCreditsExceeded, } }) } From f00e8c5ef07364c35633fc39c2aa312fb7464504 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Tue, 15 Apr 2025 14:05:13 +0100 Subject: [PATCH 10/12] not needed css --- .../builder/src/components/common/CodeEditor/AIGen.svelte | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/builder/src/components/common/CodeEditor/AIGen.svelte b/packages/builder/src/components/common/CodeEditor/AIGen.svelte index 36baa96d38..b03aff7aba 100644 --- a/packages/builder/src/components/common/CodeEditor/AIGen.svelte +++ b/packages/builder/src/components/common/CodeEditor/AIGen.svelte @@ -424,8 +424,4 @@ filter: grayscale(1) brightness(1.5); opacity: 0.5; } - - :global(.ai-gen-container[expandedonly]) .ai-icon { - cursor: default; - } From 76ae562bd2fb54746bf1bb37d865a813c227389a Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Tue, 15 Apr 2025 14:32:37 +0100 Subject: [PATCH 11/12] Remove LLM options that aren't OpenAI. --- packages/pro | 2 +- .../server/src/api/routes/tests/ai.spec.ts | 189 ++++++++---------- .../src/tests/utilities/mocks/ai/anthropic.ts | 48 ----- packages/types/src/documents/global/config.ts | 8 +- 4 files changed, 88 insertions(+), 159 deletions(-) delete mode 100644 packages/server/src/tests/utilities/mocks/ai/anthropic.ts diff --git a/packages/pro b/packages/pro index 8bcb90edac..1104c58a8c 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit 8bcb90edac3d44c48eaecf526cedc82418035438 +Subproject commit 1104c58a8c225b4dfc10998ef87ebdba347f7863 diff --git a/packages/server/src/api/routes/tests/ai.spec.ts b/packages/server/src/api/routes/tests/ai.spec.ts index 9e041a619e..58b538a2b1 100644 --- a/packages/server/src/api/routes/tests/ai.spec.ts +++ b/packages/server/src/api/routes/tests/ai.spec.ts @@ -13,8 +13,6 @@ import { } from "@budibase/types" import { context } from "@budibase/backend-core" import { mocks } from "@budibase/backend-core/tests" -import { MockLLMResponseFn } from "../../../tests/utilities/mocks/ai" -import { mockAnthropicResponse } from "../../../tests/utilities/mocks/ai/anthropic" import { quotas } from "@budibase/pro" function dedent(str: string) { @@ -30,7 +28,6 @@ type SetupFn = ( interface TestSetup { name: string setup: SetupFn - mockLLMResponse: MockLLMResponseFn } function budibaseAI(): SetupFn { @@ -89,25 +86,14 @@ const allProviders: TestSetup[] = [ OPENAI_API_KEY: "test-key", }) }, - mockLLMResponse: mockChatGPTResponse, }, { name: "OpenAI API key with custom config", setup: customAIConfig({ provider: "OpenAI", defaultModel: "gpt-4o-mini" }), - mockLLMResponse: mockChatGPTResponse, - }, - { - name: "Anthropic API key with custom config", - setup: customAIConfig({ - provider: "Anthropic", - defaultModel: "claude-3-5-sonnet-20240620", - }), - mockLLMResponse: mockAnthropicResponse, }, { name: "BudibaseAI", setup: budibaseAI(), - mockLLMResponse: mockChatGPTResponse, }, ] @@ -126,56 +112,54 @@ describe("AI", () => { nock.cleanAll() }) - describe.each(allProviders)( - "provider: $name", - ({ setup, mockLLMResponse }: TestSetup) => { - let cleanup: () => Promise | void - beforeAll(async () => { - cleanup = await setup(config) + describe.each(allProviders)("provider: $name", ({ setup }: TestSetup) => { + let cleanup: () => Promise | void + beforeAll(async () => { + cleanup = await setup(config) + }) + + afterAll(async () => { + const maybePromise = cleanup() + if (maybePromise) { + await maybePromise + } + }) + + describe("POST /api/ai/js", () => { + let cleanup: () => void + beforeAll(() => { + cleanup = features.testutils.setFeatureFlags("*", { + AI_JS_GENERATION: true, + }) }) - afterAll(async () => { - const maybePromise = cleanup() - if (maybePromise) { - await maybePromise - } + afterAll(() => { + cleanup() }) - describe("POST /api/ai/js", () => { - let cleanup: () => void - beforeAll(() => { - cleanup = features.testutils.setFeatureFlags("*", { - AI_JS_GENERATION: true, - }) - }) + it("handles correct plain code response", async () => { + mockChatGPTResponse(`return 42`) - afterAll(() => { - cleanup() - }) + const { code } = await config.api.ai.generateJs({ prompt: "test" }) + expect(code).toBe("return 42") + }) - it("handles correct plain code response", async () => { - mockLLMResponse(`return 42`) - - const { code } = await config.api.ai.generateJs({ prompt: "test" }) - expect(code).toBe("return 42") - }) - - it("handles correct markdown code response", async () => { - mockLLMResponse( - dedent(` + it("handles correct markdown code response", async () => { + mockChatGPTResponse( + dedent(` \`\`\`js return 42 \`\`\` `) - ) + ) - const { code } = await config.api.ai.generateJs({ prompt: "test" }) - expect(code).toBe("return 42") - }) + const { code } = await config.api.ai.generateJs({ prompt: "test" }) + expect(code).toBe("return 42") + }) - it("handles multiple markdown code blocks returned", async () => { - mockLLMResponse( - dedent(` + it("handles multiple markdown code blocks returned", async () => { + mockChatGPTResponse( + dedent(` This: \`\`\`js @@ -188,63 +172,62 @@ describe("AI", () => { return 10 \`\`\` `) - ) + ) - const { code } = await config.api.ai.generateJs({ prompt: "test" }) - expect(code).toBe("return 42") - }) - - // TODO: handle when this happens - it.skip("handles no code response", async () => { - mockLLMResponse("I'm sorry, you're quite right, etc.") - const { code } = await config.api.ai.generateJs({ prompt: "test" }) - expect(code).toBe("") - }) - - it("handles LLM errors", async () => { - mockLLMResponse(() => { - throw new Error("LLM error") - }) - await config.api.ai.generateJs({ prompt: "test" }, { status: 500 }) - }) + const { code } = await config.api.ai.generateJs({ prompt: "test" }) + expect(code).toBe("return 42") }) - describe("POST /api/ai/cron", () => { - it("handles correct cron response", async () => { - mockLLMResponse("0 0 * * *") + // TODO: handle when this happens + it.skip("handles no code response", async () => { + mockChatGPTResponse("I'm sorry, you're quite right, etc.") + const { code } = await config.api.ai.generateJs({ prompt: "test" }) + expect(code).toBe("") + }) - const { message } = await config.api.ai.generateCron({ + it("handles LLM errors", async () => { + mockChatGPTResponse(() => { + throw new Error("LLM error") + }) + await config.api.ai.generateJs({ prompt: "test" }, { status: 500 }) + }) + }) + + describe("POST /api/ai/cron", () => { + it("handles correct cron response", async () => { + mockChatGPTResponse("0 0 * * *") + + const { message } = await config.api.ai.generateCron({ + prompt: "test", + }) + expect(message).toBe("0 0 * * *") + }) + + it("handles expected LLM error", async () => { + mockChatGPTResponse("Error generating cron: skill issue") + + await config.api.ai.generateCron( + { prompt: "test", - }) - expect(message).toBe("0 0 * * *") - }) - - it("handles expected LLM error", async () => { - mockLLMResponse("Error generating cron: skill issue") - - await config.api.ai.generateCron( - { - prompt: "test", - }, - { status: 400 } - ) - }) - - it("handles unexpected LLM error", async () => { - mockLLMResponse(() => { - throw new Error("LLM error") - }) - - await config.api.ai.generateCron( - { - prompt: "test", - }, - { status: 500 } - ) - }) + }, + { status: 400 } + ) }) - } - ) + + it("handles unexpected LLM error", async () => { + mockChatGPTResponse(() => { + throw new Error("LLM error") + }) + + await config.api.ai.generateCron( + { + prompt: "test", + }, + { status: 500 } + ) + }) + }) + }) }) describe("BudibaseAI", () => { diff --git a/packages/server/src/tests/utilities/mocks/ai/anthropic.ts b/packages/server/src/tests/utilities/mocks/ai/anthropic.ts deleted file mode 100644 index ff0413aee1..0000000000 --- a/packages/server/src/tests/utilities/mocks/ai/anthropic.ts +++ /dev/null @@ -1,48 +0,0 @@ -import AnthropicClient from "@anthropic-ai/sdk" -import nock from "nock" -import { MockLLMResponseFn, MockLLMResponseOpts } from "." - -let chatID = 1 -const SPACE_REGEX = /\s+/g - -export const mockAnthropicResponse: MockLLMResponseFn = ( - answer: string | ((prompt: string) => string), - opts?: MockLLMResponseOpts -) => { - return nock(opts?.host || "https://api.anthropic.com") - .post("/v1/messages") - .reply((uri: string, body: nock.Body) => { - const req = body as AnthropicClient.MessageCreateParamsNonStreaming - const prompt = req.messages[0].content - if (typeof prompt !== "string") { - throw new Error("Anthropic mock only supports string prompts") - } - - let content - if (typeof answer === "function") { - try { - content = answer(prompt) - } catch (e) { - return [500, "Internal Server Error"] - } - } else { - content = answer - } - - const resp: AnthropicClient.Messages.Message = { - id: `${chatID++}`, - type: "message", - role: "assistant", - model: req.model, - stop_reason: "end_turn", - usage: { - input_tokens: prompt.split(SPACE_REGEX).length, - output_tokens: content.split(SPACE_REGEX).length, - }, - stop_sequence: null, - content: [{ type: "text", text: content }], - } - return [200, resp] - }) - .persist() -} diff --git a/packages/types/src/documents/global/config.ts b/packages/types/src/documents/global/config.ts index 422486e30f..3c311c5a86 100644 --- a/packages/types/src/documents/global/config.ts +++ b/packages/types/src/documents/global/config.ts @@ -111,13 +111,7 @@ export interface SCIMInnerConfig { export interface SCIMConfig extends Config {} -export type AIProvider = - | "OpenAI" - | "Anthropic" - | "AzureOpenAI" - | "TogetherAI" - | "Custom" - | "BudibaseAI" +export type AIProvider = "OpenAI" | "AzureOpenAI" | "BudibaseAI" export interface ProviderConfig { provider: AIProvider From 9272f3dc5f4aa4e208084cdfcbe608bd6d40ec47 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Tue, 15 Apr 2025 15:35:06 +0100 Subject: [PATCH 12/12] Update pro reference. --- packages/pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pro b/packages/pro index 1104c58a8c..f27612865c 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit 1104c58a8c225b4dfc10998ef87ebdba347f7863 +Subproject commit f27612865cd5f689b75b8f4e148293dff3b77bc4