fix(ai-builder): Allow Anthropic key to be used in local development (no-changelog) (#20772)

This commit is contained in:
Michael Drury
2025-10-16 09:00:50 +01:00
committed by GitHub
parent f41b86402e
commit b2cd23b7f7
19 changed files with 91 additions and 112 deletions
@@ -136,6 +136,7 @@ export interface FrontendSettings {
};
aiBuilder: {
enabled: boolean;
setup: boolean;
};
deployment: {
type: string;
@@ -0,0 +1,8 @@
import { Config, Env } from '../decorators';
@Config
export class AiBuilderConfig {
/** Keys for local service */
@Env('N8N_AI_ANTHROPIC_KEY')
apiKey: string = '';
}
+4
View File
@@ -1,6 +1,7 @@
import { z } from 'zod';
import { AiAssistantConfig } from './configs/ai-assistant.config';
import { AiBuilderConfig } from './configs/ai-builder.config';
import { AiConfig } from './configs/ai.config';
import { AuthConfig } from './configs/auth.config';
import { CacheConfig } from './configs/cache.config';
@@ -152,6 +153,9 @@ export class GlobalConfig {
@Nested
aiAssistant: AiAssistantConfig;
@Nested
aiBuilder: AiBuilderConfig;
@Nested
tags: TagsConfig;
+3
View File
@@ -347,6 +347,9 @@ describe('GlobalConfig', () => {
aiAssistant: {
baseUrl: '',
},
aiBuilder: {
apiKey: '',
},
tags: {
disabled: false,
},
@@ -284,6 +284,7 @@ describe('FrontendService', () => {
expect(service.settings.aiBuilder).toEqual({
enabled: false,
setup: false,
});
});
@@ -268,6 +268,7 @@ export class FrontendService {
},
aiBuilder: {
enabled: false,
setup: false,
},
aiCredits: {
enabled: false,
@@ -425,7 +426,11 @@ export class FrontendService {
this.settings.aiCredits.credits = this.license.getAiCredits();
}
this.settings.aiBuilder.enabled = isAiBuilderEnabled;
if (isAiBuilderEnabled) {
this.settings.aiBuilder.enabled = isAiBuilderEnabled;
this.settings.aiBuilder.setup =
!!this.globalConfig.aiAssistant.baseUrl || !!this.globalConfig.aiBuilder.apiKey;
}
this.settings.mfa.enabled = this.globalConfig.mfa.enabled;
@@ -137,6 +137,7 @@ export const defaultSettings: FrontendSettings = {
},
aiBuilder: {
enabled: false,
setup: false,
},
workflowHistory: {
pruneTime: 0,
@@ -6,7 +6,7 @@ import { mockedStore } from '@/__tests__/utils';
import { createComponentRenderer } from '@/__tests__/render';
import type { IExecutionResponse } from '@/Interface';
import NodeErrorView from '@/components/Error/NodeErrorView.vue';
import { useAssistantStore } from '@/features/assistant/assistant.store';
import { useChatPanelStore } from '@/features/assistant/chatPanel.store';
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
import { useNDVStore } from '@/stores/ndv.store';
import { useWorkflowsStore } from '@/stores/workflows.store';
@@ -29,7 +29,7 @@ Object.defineProperty(window, 'open', {
writable: true,
});
let mockAiAssistantStore: ReturnType<typeof mockedStore<typeof useAssistantStore>>;
let mockChatPanelStore: ReturnType<typeof mockedStore<typeof useChatPanelStore>>;
let mockNodeTypeStore: ReturnType<typeof mockedStore<typeof useNodeTypesStore>>;
let mockNDVStore: ReturnType<typeof mockedStore<typeof useNDVStore>>;
let mockWorkflowsStore: ReturnType<typeof mockedStore<typeof useWorkflowsStore>>;
@@ -41,7 +41,7 @@ describe('NodeErrorView.vue', () => {
beforeEach(() => {
createTestingPinia();
mockAiAssistantStore = mockedStore(useAssistantStore);
mockChatPanelStore = mockedStore(useChatPanelStore);
mockNodeTypeStore = mockedStore(useNodeTypesStore);
mockNDVStore = mockedStore(useNDVStore);
mockWorkflowsStore = mockedStore(useWorkflowsStore);
@@ -118,7 +118,7 @@ describe('NodeErrorView.vue', () => {
hidden: true,
}));
mockAiAssistantStore.canShowAssistantButtonsOnCanvas = true;
mockChatPanelStore.canShowAiButtonOnCanvas = true;
const { queryByTestId } = renderComponent({
props: {
@@ -177,7 +177,7 @@ describe('NodeErrorView.vue', () => {
});
it('does not renders open node button when the error is in sub node', () => {
mockAiAssistantStore.canShowAssistantButtonsOnCanvas = true;
mockChatPanelStore.canShowAiButtonOnCanvas = true;
const { getByTestId, queryByTestId } = renderComponent({
props: {
error,
@@ -139,7 +139,7 @@ const isAskAssistantAvailable = computed(() => {
}
const isCustomNode = node.value.type === undefined || isCommunityPackageName(node.value.type);
return assistantStore.canShowAssistantButtonsOnCanvas && !isCustomNode && !nodeIsHidden();
return chatPanelStore.canShowAiButtonOnCanvas && !isCustomNode && !nodeIsHidden();
});
const assistantAlreadyAsked = computed(() => {
@@ -780,6 +780,7 @@ async function onAskAssistantClick() {
width: 100%;
overflow: auto;
background: var(--color--background--light-2);
code {
font-size: var(--font-size--sm);
}
@@ -191,7 +191,7 @@ function openCommandBar(event: MouseEvent) {
@click="toggleFocusPanel"
/>
</KeyboardShortcutTooltip>
<N8nTooltip v-if="assistantStore.canShowAssistantButtonsOnCanvas" placement="left">
<N8nTooltip v-if="chatPanelStore.canShowAiButtonOnCanvas" placement="left">
<template #content> {{ i18n.baseText('aiAssistant.tooltip') }}</template>
<N8nButton
type="tertiary"
@@ -287,8 +287,6 @@ describe('AI Assistant store', () => {
setAssistantEnabled(false);
expect(assistantStore.isAssistantEnabled).toBe(false);
expect(assistantStore.canShowAssistant).toBe(false);
expect(assistantStore.canShowAssistantButtonsOnCanvas).toBe(false);
});
it('should show assistant if all conditions are met', () => {
@@ -296,8 +294,6 @@ describe('AI Assistant store', () => {
setAssistantEnabled(true);
expect(assistantStore.isAssistantEnabled).toBe(true);
expect(assistantStore.canShowAssistant).toBe(true);
expect(assistantStore.canShowAssistantButtonsOnCanvas).toBe(true);
});
it('should not show assistant if on a settings page', () => {
@@ -306,8 +302,6 @@ describe('AI Assistant store', () => {
setAssistantEnabled(true);
expect(assistantStore.isAssistantEnabled).toBe(true);
expect(assistantStore.canShowAssistant).toBe(false);
expect(assistantStore.canShowAssistantButtonsOnCanvas).toBe(false);
});
[VIEWS.PROJECTS_CREDENTIALS, VIEWS.TEMPLATE_SETUP, VIEWS.CREDENTIALS].forEach((view) => {
@@ -317,8 +311,6 @@ describe('AI Assistant store', () => {
setAssistantEnabled(true);
expect(assistantStore.isAssistantEnabled).toBe(true);
expect(assistantStore.canShowAssistant).toBe(true);
expect(assistantStore.canShowAssistantButtonsOnCanvas).toBe(false);
});
});
@@ -344,7 +336,6 @@ describe('AI Assistant store', () => {
const assistantStore = useAssistantStore();
setAssistantEnabled(true);
expect(assistantStore.canShowAssistantButtonsOnCanvas).toBe(true);
expect(assistantStore.hideAssistantFloatingButton).toBe(false);
});
});
@@ -359,7 +350,6 @@ describe('AI Assistant store', () => {
const assistantStore = useAssistantStore();
setAssistantEnabled(true);
expect(assistantStore.canShowAssistantButtonsOnCanvas).toBe(true);
expect(assistantStore.hideAssistantFloatingButton).toBe(true);
});
});
@@ -86,22 +86,8 @@ export const useAssistantStore = defineStore(STORES.ASSISTANT, () => {
return !sessionStarted || sessionExplicitlyEnded;
});
const isAssistantOpen = computed(
() =>
canShowAssistant.value &&
chatPanelStateStore.isOpen &&
chatPanelStateStore.activeMode === 'assistant',
);
const isAssistantEnabled = computed(() => settings.isAiAssistantEnabled);
const canShowAssistant = computed(
() => isAssistantEnabled.value && ENABLED_VIEWS.includes(route.name as VIEWS),
);
const canShowAssistantButtonsOnCanvas = computed(
() => isAssistantEnabled.value && EDITABLE_CANVAS_VIEWS.includes(route.name as VIEWS),
);
const hideAssistantFloatingButton = computed(
() => EDITABLE_CANVAS_VIEWS.includes(route.name as VIEWS) && !workflowsStore.activeNode(),
);
@@ -115,9 +101,10 @@ export const useAssistantStore = defineStore(STORES.ASSISTANT, () => {
const isFloatingButtonShown = computed(
() =>
canShowAssistantButtonsOnCanvas.value &&
!isAssistantOpen.value &&
!hideAssistantFloatingButton.value,
!chatPanelStateStore.isOpen &&
!hideAssistantFloatingButton.value &&
isAssistantEnabled.value &&
EDITABLE_CANVAS_VIEWS.includes(route.name as VIEWS),
);
function resetAssistantChat() {
@@ -782,13 +769,10 @@ export const useAssistantStore = defineStore(STORES.ASSISTANT, () => {
return {
isAssistantEnabled,
canShowAssistantButtonsOnCanvas,
hideAssistantFloatingButton,
chatMessages,
unreadCount,
streaming,
isAssistantOpen,
canShowAssistant,
currentSessionId,
lastUnread,
isSessionEnded,
@@ -88,14 +88,6 @@ spy.mockImplementation(
}) as unknown as Telemetry,
);
const setAssistantEnabled = (enabled: boolean) => {
settingsStore.setSettings(
merge({}, defaultSettings, {
aiAssistant: { enabled, setup: true },
}),
);
};
const currentRouteName = ENABLED_VIEWS[0];
vi.mock('vue-router', () => ({
useRoute: vi.fn(() =>
@@ -287,7 +279,7 @@ describe('AI Builder store', () => {
builderStore.sendChatMessage({ text: 'Add nodes and connect them' });
// Initially shows "aiAssistant.thinkingSteps.thinking" from prepareForStreaming
expect(builderStore.assistantThinkingMessage).toBe('aiAssistant.thinkingSteps.thinking');
expect(builderStore.builderThinkingMessage).toBe('aiAssistant.thinkingSteps.thinking');
// First tool starts
onMessageCallback({
@@ -304,7 +296,7 @@ describe('AI Builder store', () => {
});
// Should show "aiAssistant.thinkingSteps.thinking"
expect(builderStore.assistantThinkingMessage).toBe('aiAssistant.thinkingSteps.thinking');
expect(builderStore.builderThinkingMessage).toBe('aiAssistant.thinkingSteps.thinking');
// Second tool starts (different toolCallId)
onMessageCallback({
@@ -321,7 +313,7 @@ describe('AI Builder store', () => {
});
// Still showing "aiAssistant.thinkingSteps.thinking" with multiple tools
expect(builderStore.assistantThinkingMessage).toBe('aiAssistant.thinkingSteps.thinking');
expect(builderStore.builderThinkingMessage).toBe('aiAssistant.thinkingSteps.thinking');
// First tool completes
onMessageCallback({
@@ -338,7 +330,7 @@ describe('AI Builder store', () => {
});
// Still "aiAssistant.thinkingSteps.thinking" because second tool is still running
expect(builderStore.assistantThinkingMessage).toBe('aiAssistant.thinkingSteps.thinking');
expect(builderStore.builderThinkingMessage).toBe('aiAssistant.thinkingSteps.thinking');
// Second tool completes
onMessageCallback({
@@ -355,14 +347,14 @@ describe('AI Builder store', () => {
});
// Now should show "aiAssistant.thinkingSteps.thinking" because all tools completed
expect(builderStore.assistantThinkingMessage).toBe('aiAssistant.thinkingSteps.thinking');
expect(builderStore.builderThinkingMessage).toBe('aiAssistant.thinkingSteps.thinking');
// Call onDone to stop streaming
onDoneCallback();
// Message should persist after streaming ends
expect(builderStore.streaming).toBe(false);
expect(builderStore.assistantThinkingMessage).toBe('aiAssistant.thinkingSteps.thinking');
expect(builderStore.builderThinkingMessage).toBe('aiAssistant.thinkingSteps.thinking');
vi.useRealTimers();
});
@@ -404,12 +396,12 @@ describe('AI Builder store', () => {
// Should show "aiAssistant.thinkingSteps.thinking" when tool completes
await vi.waitFor(() =>
expect(builderStore.assistantThinkingMessage).toBe('aiAssistant.thinkingSteps.thinking'),
expect(builderStore.builderThinkingMessage).toBe('aiAssistant.thinkingSteps.thinking'),
);
// Should still show "aiAssistant.thinkingSteps.thinking" after workflow-updated
await vi.waitFor(() => expect(builderStore.chatMessages).toHaveLength(3)); // user + tool + workflow
expect(builderStore.assistantThinkingMessage).toBe('aiAssistant.thinkingSteps.thinking');
expect(builderStore.builderThinkingMessage).toBe('aiAssistant.thinkingSteps.thinking');
// Verify streaming has ended
expect(builderStore.streaming).toBe(false);
@@ -441,25 +433,7 @@ describe('AI Builder store', () => {
builderStore.resetBuilderChat();
expect(builderStore.chatMessages).toEqual([]);
expect(builderStore.assistantThinkingMessage).toBeUndefined();
});
it('should not show builder if disabled in settings', () => {
const builderStore = useBuilderStore();
setAssistantEnabled(false);
expect(builderStore.isAssistantEnabled).toBe(false);
expect(builderStore.canShowAssistant).toBe(false);
expect(builderStore.canShowAssistantButtonsOnCanvas).toBe(false);
});
it('should show builder if all conditions are met', () => {
const builderStore = useBuilderStore();
setAssistantEnabled(true);
expect(builderStore.isAssistantEnabled).toBe(true);
expect(builderStore.canShowAssistant).toBe(true);
expect(builderStore.canShowAssistantButtonsOnCanvas).toBe(true);
expect(builderStore.builderThinkingMessage).toBeUndefined();
});
describe('isAIBuilderEnabled computed property', () => {
@@ -737,7 +711,7 @@ describe('AI Builder store', () => {
// Verify streaming state was reset
expect(builderStore.streaming).toBe(false);
expect(builderStore.assistantThinkingMessage).toBeUndefined();
expect(builderStore.builderThinkingMessage).toBeUndefined();
});
it('should abort previous request when sending new message', () => {
@@ -3,7 +3,6 @@ import {
DEFAULT_NEW_WORKFLOW_NAME,
WORKFLOW_BUILDER_DEPRECATED_EXPERIMENT,
WORKFLOW_BUILDER_RELEASE_EXPERIMENT,
EDITABLE_CANVAS_VIEWS,
PLACEHOLDER_EMPTY_WORKFLOW_ID,
} from '@/constants';
import { BUILDER_ENABLED_VIEWS } from './constants';
@@ -32,7 +31,6 @@ import { useNodeTypesStore } from '@/stores/nodeTypes.store';
import { useCredentialsStore } from '@/features/credentials/credentials.store';
import { getAuthTypeForNodeCredential, getMainAuthField } from '@/utils/nodeTypesUtils';
import { stringSizeInBytes } from '@/utils/typesUtils';
import { useChatPanelStateStore } from './chatPanelState.store';
const INFINITE_CREDITS = -1;
export const ENABLED_VIEWS = BUILDER_ENABLED_VIEWS;
@@ -41,7 +39,7 @@ export const useBuilderStore = defineStore(STORES.BUILDER, () => {
// Core state
const chatMessages = ref<ChatUI.AssistantMessage[]>([]);
const streaming = ref<boolean>(false);
const assistantThinkingMessage = ref<string | undefined>();
const builderThinkingMessage = ref<string | undefined>();
const streamingAbortController = ref<AbortController | null>(null);
const initialGeneration = ref<boolean>(false);
const creditsQuota = ref<number | undefined>();
@@ -49,7 +47,6 @@ export const useBuilderStore = defineStore(STORES.BUILDER, () => {
const hasMessages = ref<boolean>(false);
// Store dependencies
const chatPanelStateStore = useChatPanelStateStore();
const settings = useSettingsStore();
const rootStore = useRootStore();
const workflowsStore = useWorkflowsStore();
@@ -74,9 +71,6 @@ export const useBuilderStore = defineStore(STORES.BUILDER, () => {
getRunningTools,
} = useBuilderMessages();
// Computed properties
const isAssistantEnabled = computed(() => settings.isAiAssistantEnabled);
const trackingSessionId = computed(() => rootStore.pushRef);
const workflowPrompt = computed(() => {
@@ -86,20 +80,6 @@ export const useBuilderStore = defineStore(STORES.BUILDER, () => {
return firstUserMessage?.content;
});
const canShowAssistant = computed(
() => isAssistantEnabled.value && ENABLED_VIEWS.includes(route.name as VIEWS),
);
const canShowAssistantButtonsOnCanvas = computed(
() => isAssistantEnabled.value && EDITABLE_CANVAS_VIEWS.includes(route.name as VIEWS),
);
const isAssistantOpen = computed(
() =>
canShowAssistant.value &&
chatPanelStateStore.isOpen &&
chatPanelStateStore.activeMode === 'builder',
);
const isAIBuilderEnabled = computed(() => {
// Check license first
@@ -156,13 +136,13 @@ export const useBuilderStore = defineStore(STORES.BUILDER, () => {
*/
function resetBuilderChat() {
chatMessages.value = clearMessages();
assistantThinkingMessage.value = undefined;
builderThinkingMessage.value = undefined;
initialGeneration.value = false;
}
// Message handling functions
function addLoadingAssistantMessage(message: string) {
assistantThinkingMessage.value = message;
builderThinkingMessage.value = message;
}
function stopStreaming() {
@@ -184,7 +164,7 @@ export const useBuilderStore = defineStore(STORES.BUILDER, () => {
assert(e instanceof Error);
stopStreaming();
assistantThinkingMessage.value = undefined;
builderThinkingMessage.value = undefined;
if (e.name === 'AbortError') {
// Handle abort errors as they are expected when stopping streaming
@@ -346,10 +326,10 @@ export const useBuilderStore = defineStore(STORES.BUILDER, () => {
chatMessages.value = result.messages;
if (result.shouldClearThinking) {
assistantThinkingMessage.value = undefined;
builderThinkingMessage.value = undefined;
} else {
// Always update thinking message, even when undefined (to clear it)
assistantThinkingMessage.value = result.thinkingMessage;
builderThinkingMessage.value = result.thinkingMessage;
}
},
() => stopStreaming(),
@@ -579,13 +559,9 @@ export const useBuilderStore = defineStore(STORES.BUILDER, () => {
// Public API
return {
// State
isAssistantEnabled,
canShowAssistantButtonsOnCanvas,
chatMessages,
streaming,
isAssistantOpen,
canShowAssistant,
assistantThinkingMessage,
builderThinkingMessage,
isAIBuilderEnabled,
workflowPrompt,
toolMessages,
@@ -3,12 +3,13 @@ import { defineStore } from 'pinia';
import { STORES } from '@n8n/stores';
import { useUIStore } from '@/stores/ui.store';
import { useRoute } from 'vue-router';
import { ASK_AI_SLIDE_OUT_DURATION_MS } from '@/constants';
import { ASK_AI_SLIDE_OUT_DURATION_MS, EDITABLE_CANVAS_VIEWS } from '@/constants';
import type { VIEWS } from '@/constants';
import { ASSISTANT_ENABLED_VIEWS, BUILDER_ENABLED_VIEWS } from './constants';
import { useChatPanelStateStore, type ChatPanelMode } from './chatPanelState.store';
import { useAssistantStore } from './assistant.store';
import { useBuilderStore } from './builder.store';
import { useSettingsStore } from '@/stores/settings.store';
import type { ICredentialType } from 'n8n-workflow';
import type { ChatRequest } from './assistant.types';
@@ -31,11 +32,18 @@ export const useChatPanelStore = defineStore(STORES.CHAT_PANEL, () => {
const uiStore = useUIStore();
const route = useRoute();
const chatPanelStateStore = useChatPanelStateStore();
const settingsStore = useSettingsStore();
// Computed
const isAssistantModeActive = computed(() => chatPanelStateStore.activeMode === 'assistant');
const isBuilderModeActive = computed(() => chatPanelStateStore.activeMode === 'builder');
const canShowAiButtonOnCanvas = computed(
() =>
settingsStore.isAiAssistantOrBuilderEnabled &&
EDITABLE_CANVAS_VIEWS.includes(route.name as VIEWS),
);
// Actions
async function open(options?: { mode?: ChatPanelMode }) {
const mode = options?.mode;
@@ -183,6 +191,7 @@ export const useChatPanelStore = defineStore(STORES.CHAT_PANEL, () => {
// Computed
isAssistantModeActive,
isBuilderModeActive,
canShowAiButtonOnCanvas,
// Actions
open,
close,
@@ -51,7 +51,7 @@ const loadingMessage = computed(() => {
return undefined;
}
return builderStore.assistantThinkingMessage;
return builderStore.builderThinkingMessage;
});
const currentRoute = computed(() => route.name);
const showExecuteMessage = computed(() => {
@@ -2,8 +2,9 @@
import { useBuilderStore } from '@/features/assistant/builder.store';
import { useChatPanelStore } from '@/features/assistant/chatPanel.store';
import { useAssistantStore } from '@/features/assistant/assistant.store';
import { useSettingsStore } from '@/stores/settings.store';
import { useDebounce } from '@/composables/useDebounce';
import { computed, onBeforeUnmount, ref } from 'vue';
import { computed, onBeforeUnmount, onMounted, ref } from 'vue';
import SlideTransition from '@/components/transitions/SlideTransition.vue';
import AskAssistantBuild from './Agent/AskAssistantBuild.vue';
import AskAssistantChat from './Chat/AskAssistantChat.vue';
@@ -17,6 +18,7 @@ import HubSwitcher from '@/features/assistant/components/HubSwitcher.vue';
const builderStore = useBuilderStore();
const assistantStore = useAssistantStore();
const chatPanelStore = useChatPanelStore();
const settingsStore = useSettingsStore();
const route = useRoute();
const askAssistantBuildRef = ref<InstanceType<typeof AskAssistantBuild>>();
@@ -29,6 +31,7 @@ const chatWidth = computed(() => chatPanelStore.width);
const canToggleModes = computed(() => {
const currentRoute = route?.name;
return (
settingsStore.isAiAssistantEnabled &&
builderStore.isAIBuilderEnabled &&
currentRoute &&
BUILDER_ENABLED_VIEWS.includes(currentRoute as VIEWS)
@@ -95,6 +98,20 @@ const unsubscribeBuilderStore = builderStore.$onAction(({ name }) => {
}
});
// Set default mode based on which flags are enabled
onMounted(() => {
// If toggle is not available (only one mode enabled), lock to the appropriate mode
if (!canToggleModes.value) {
if (builderStore.isAIBuilderEnabled) {
// Only builder is enabled, lock to builder mode
chatPanelStore.switchMode('builder');
} else if (settingsStore.isAiAssistantEnabled) {
// Only assistant is enabled, lock to assistant mode
chatPanelStore.switchMode('assistant');
}
}
});
onBeforeUnmount(() => {
unsubscribeAssistantStore();
unsubscribeBuilderStore();
@@ -91,7 +91,13 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, () => {
const isAskAiEnabled = computed(() => settings.value.askAi?.enabled);
const isAiBuilderEnabled = computed(() => settings.value.aiBuilder?.enabled);
const isAiBuilderEnabled = computed(
() => settings.value.aiBuilder?.enabled && settings.value.aiBuilder?.setup,
);
const isAiAssistantOrBuilderEnabled = computed(
() => isAiAssistantEnabled.value || isAiBuilderEnabled.value,
);
const showSetupPage = computed(() => userManagement.value.showSetupOnFirstLoad);
@@ -362,6 +368,7 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, () => {
isCommunityPlan,
isAskAiEnabled,
isAiBuilderEnabled,
isAiAssistantOrBuilderEnabled,
isAiCreditsEnabled,
aiCreditsQuota,
reset,
@@ -1817,9 +1817,7 @@ watch(
};
fallbackNodes.value =
builderStore.isAIBuilderEnabled &&
builderStore.isAssistantEnabled &&
builderStore.assistantMessages.length === 0
builderStore.isAIBuilderEnabled && builderStore.assistantMessages.length === 0
? [choicePromptItem]
: [addNodesItem];
},