chore(ai-builder): Rollout builder experiment, removing PostHog flags (#21079)

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Mutasem Aldmour 2025-10-23 13:17:10 +02:00 committed by GitHub
parent 4698b93a5a
commit 9fc867ce47
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 39 additions and 409 deletions

View File

@ -49,28 +49,18 @@ export class AiWorkflowBuilderService {
});
}
private async getApiProxyAuthHeaders(user: IUser, useDeprecatedCredentials = false) {
private async getApiProxyAuthHeaders(user: IUser) {
assert(this.client);
let authHeaders: { Authorization: string };
if (useDeprecatedCredentials) {
const authResponse = await this.client.generateApiProxyCredentials(user);
authHeaders = { Authorization: authResponse.apiKey };
} else {
const authResponse = await this.client.getBuilderApiProxyToken(user);
authHeaders = {
Authorization: `${authResponse.tokenType} ${authResponse.accessToken}`,
};
}
const authResponse = await this.client.getBuilderApiProxyToken(user);
const authHeaders = {
Authorization: `${authResponse.tokenType} ${authResponse.accessToken}`,
};
return authHeaders;
}
private async setupModels(
user: IUser,
useDeprecatedCredentials = false,
): Promise<{
private async setupModels(user: IUser): Promise<{
anthropicClaude: ChatAnthropic;
tracingClient?: TracingClient;
authHeaders?: { Authorization: string };
@ -78,7 +68,7 @@ export class AiWorkflowBuilderService {
try {
// If client is provided, use it for API proxy
if (this.client) {
const authHeaders = await this.getApiProxyAuthHeaders(user, useDeprecatedCredentials);
const authHeaders = await this.getApiProxyAuthHeaders(user);
// Extract baseUrl from client configuration
const baseUrl = this.client.getApiProxyBaseUrl();
@ -154,11 +144,8 @@ export class AiWorkflowBuilderService {
});
}
private async getAgent(user: IUser, useDeprecatedCredentials = false) {
const { anthropicClaude, tracingClient, authHeaders } = await this.setupModels(
user,
useDeprecatedCredentials,
);
private async getAgent(user: IUser) {
const { anthropicClaude, tracingClient, authHeaders } = await this.setupModels(user);
const agent = new WorkflowBuilderAgent({
parsedNodeTypes: this.parsedNodeTypes,
@ -172,9 +159,7 @@ export class AiWorkflowBuilderService {
: undefined,
instanceUrl: this.instanceUrl,
onGenerationSuccess: async () => {
if (!useDeprecatedCredentials) {
await this.onGenerationSuccess(user, authHeaders);
}
await this.onGenerationSuccess(user, authHeaders);
},
});
@ -204,7 +189,7 @@ export class AiWorkflowBuilderService {
}
async *chat(payload: ChatPayload, user: IUser, abortSignal?: AbortSignal) {
const agent = await this.getAgent(user, payload.useDeprecatedCredentials);
const agent = await this.getAgent(user);
for await (const output of agent.chat(payload, user?.id?.toString(), abortSignal)) {
yield output;

View File

@ -278,7 +278,6 @@ describe('AiWorkflowBuilderService', () => {
workflowContext: {
currentWorkflow: { id: 'test-workflow' },
},
useDeprecatedCredentials: false,
};
});
@ -308,19 +307,6 @@ describe('AiWorkflowBuilderService', () => {
);
});
it('should handle deprecated credentials', async () => {
const payloadWithDeprecatedCredentials = {
...mockPayload,
useDeprecatedCredentials: true,
};
const generator = service.chat(payloadWithDeprecatedCredentials, mockUser);
await generator.next();
// Verify that the deprecated credentials flow was used
expect(mockClient.generateApiProxyCredentials).toHaveBeenCalledWith(mockUser);
});
it('should create WorkflowBuilderAgent with correct configuration when using client', async () => {
const generator = service.chat(mockPayload, mockUser);
await generator.next();
@ -439,24 +425,6 @@ describe('AiWorkflowBuilderService', () => {
// Verify callback was called with correct parameters
expect(mockOnCreditsUpdated).toHaveBeenCalledWith('test-user-id', 10, 1);
});
it('should not call markBuilderSuccess when using deprecated credentials', async () => {
const payloadWithDeprecatedCredentials = {
...mockPayload,
useDeprecatedCredentials: true,
};
const generator = service.chat(payloadWithDeprecatedCredentials, mockUser);
await generator.next();
const config = MockedWorkflowBuilderAgent.mock.calls[0][0];
// Call the onGenerationSuccess callback
await config.onGenerationSuccess!();
// Should not call markBuilderSuccess for deprecated credentials
expect(mockClient.markBuilderSuccess).not.toHaveBeenCalled();
});
});
describe('getSessions', () => {
@ -580,7 +548,6 @@ describe('AiWorkflowBuilderService', () => {
workflowContext: {
currentWorkflow: { id: workflowId },
},
useDeprecatedCredentials: false,
};
// First, simulate a chat session

View File

@ -143,7 +143,6 @@ describe('WorkflowBuilderAgent', () => {
workflowContext: {
currentWorkflow: { id: 'workflow-123' },
},
useDeprecatedCredentials: false,
};
});
@ -151,7 +150,6 @@ describe('WorkflowBuilderAgent', () => {
const longMessage = 'x'.repeat(MAX_AI_BUILDER_PROMPT_LENGTH + 1);
const payload: ChatPayload = {
message: longMessage,
useDeprecatedCredentials: false,
};
await expect(async () => {
@ -169,7 +167,6 @@ describe('WorkflowBuilderAgent', () => {
const validMessage = 'Create a simple workflow';
const payload: ChatPayload = {
message: validMessage,
useDeprecatedCredentials: false,
};
// Mock the stream processing to return a proper StreamOutput

View File

@ -66,12 +66,6 @@ export interface ChatPayload {
executionData?: IRunExecutionData['resultData'];
expressionValues?: Record<string, ExpressionValue[]>;
};
/**
* Calls AI Assistant Service using deprecated credentials and endpoints
* These credentials/endpoints will soon be removed
* As new implementation is rolled out and builder experiment is released
*/
useDeprecatedCredentials?: boolean;
}
export class WorkflowBuilderAgent {

View File

@ -12,7 +12,6 @@ describe('AiBuilderChatRequestDto', () => {
connections: {},
},
},
useDeprecatedCredentials: false,
},
};
@ -316,21 +315,5 @@ describe('AiBuilderChatRequestDto', () => {
expect(result.success).toBe(false);
});
it('should validate when useDeprecatedCredentials is explicitly set', () => {
const requestWithFlag = {
payload: {
...validBasePayload.payload,
useDeprecatedCredentials: true,
},
};
const result = AiBuilderChatRequestDto.safeParse(requestWithFlag);
expect(result.success).toBe(true);
if (result.success) {
expect(result.data.payload.useDeprecatedCredentials).toBe(true);
}
});
});
});

View File

@ -57,6 +57,5 @@ export class AiBuilderChatRequestDto extends Z.class({
})
.optional(),
}),
useDeprecatedCredentials: z.boolean().default(false),
}),
}) {}

View File

@ -124,7 +124,6 @@ describe('AiController', () => {
workflowContext: {
currentWorkflow: { id: 'workflow123' },
},
useDeprecatedCredentials: false,
},
};
@ -153,7 +152,6 @@ describe('AiController', () => {
executionData: undefined,
executionSchema: undefined,
},
useDeprecatedCredentials: false,
},
request.user,
expect.any(AbortSignal),

View File

@ -55,7 +55,7 @@ export class AiController {
res.on('close', handleClose);
const { text, workflowContext, useDeprecatedCredentials } = payload.payload;
const { text, workflowContext } = payload.payload;
const aiResponse = this.workflowBuilderService.chat(
{
message: text,
@ -65,7 +65,6 @@ export class AiController {
executionSchema: workflowContext.executionSchema,
expressionValues: workflowContext.expressionValues,
},
useDeprecatedCredentials,
},
req.user,
signal,

View File

@ -50,13 +50,7 @@ describe('API: ai', () => {
expect(streamRequestSpy).toHaveBeenCalledWith(
mockContext,
'/ai/build',
{
...payload,
payload: {
...payload.payload,
useDeprecatedCredentials: false,
},
},
payload,
mockOnMessageUpdated,
mockOnDone,
mockOnError,
@ -89,56 +83,12 @@ describe('API: ai', () => {
expect(streamRequestSpy).toHaveBeenCalledWith(
mockContext,
'/ai/build',
{
...payload,
payload: {
...payload.payload,
useDeprecatedCredentials: false,
},
},
mockOnMessageUpdated,
mockOnDone,
mockOnError,
undefined,
abortSignal,
);
});
it('should use deprecated credentials when flag is true', () => {
const payload: ChatRequest.RequestPayload = {
payload: {
role: 'user',
type: 'message',
text: 'Build me a workflow',
},
sessionId: 'session-456',
};
chatWithBuilder(
mockContext,
payload,
mockOnMessageUpdated,
mockOnDone,
mockOnError,
undefined,
true,
);
expect(streamRequestSpy).toHaveBeenCalledWith(
mockContext,
'/ai/build',
{
...payload,
payload: {
...payload.payload,
useDeprecatedCredentials: true,
},
},
mockOnMessageUpdated,
mockOnDone,
mockOnError,
undefined,
undefined,
abortSignal,
);
});
@ -165,13 +115,7 @@ describe('API: ai', () => {
expect(streamRequestSpy).toHaveBeenCalledWith(
mockContext,
'/ai/build',
{
...payload,
payload: {
...payload.payload,
useDeprecatedCredentials: false,
},
},
payload,
mockOnMessageUpdated,
mockOnDone,
mockOnError,
@ -195,13 +139,7 @@ describe('API: ai', () => {
expect(streamRequestSpy).toHaveBeenCalledWith(
mockContext,
'/ai/build',
{
...payload,
payload: {
...payload.payload,
useDeprecatedCredentials: false,
},
},
payload,
mockOnMessageUpdated,
mockOnDone,
mockOnError,
@ -316,26 +254,12 @@ describe('API: ai', () => {
sessionId: 'session-complex',
};
chatWithBuilder(
mockContext,
payload,
mockOnMessageUpdated,
mockOnDone,
mockOnError,
undefined,
false,
);
chatWithBuilder(mockContext, payload, mockOnMessageUpdated, mockOnDone, mockOnError);
expect(streamRequestSpy).toHaveBeenCalledWith(
mockContext,
'/ai/build',
{
...payload,
payload: {
...payload.payload,
useDeprecatedCredentials: false,
},
},
payload,
mockOnMessageUpdated,
mockOnDone,
mockOnError,
@ -358,13 +282,7 @@ describe('API: ai', () => {
expect(streamRequestSpy).toHaveBeenCalledWith(
mockContext,
'/ai/build',
{
...payload,
payload: {
...payload.payload,
useDeprecatedCredentials: false,
},
},
payload,
mockOnMessageUpdated,
mockOnDone,
mockOnError,

View File

@ -18,7 +18,6 @@ export function chatWithBuilder(
onDone: () => void,
onError: (e: Error) => void,
abortSignal?: AbortSignal,
useDeprecatedCredentials = false,
): void {
void streamRequest<ChatRequest.ResponsePayload>(
ctx,
@ -27,7 +26,6 @@ export function chatWithBuilder(
...payload,
payload: {
...payload.payload,
useDeprecatedCredentials,
},
},
onMessageUpdated,

View File

@ -22,18 +22,6 @@ export const NDV_UI_OVERHAUL_EXPERIMENT = {
variant: 'variant',
};
export const WORKFLOW_BUILDER_RELEASE_EXPERIMENT = {
name: '043_workflow_builder_release',
control: 'control',
variant: 'variant',
};
export const WORKFLOW_BUILDER_DEPRECATED_EXPERIMENT = {
name: '036_workflow_builder_agent',
control: 'control',
variant: 'variant',
};
export const EXTRA_TEMPLATE_LINKS_EXPERIMENT = {
name: '034_extra_template_links',
control: 'control',
@ -88,8 +76,6 @@ export const PERSONALIZED_TEMPLATES_V3 = {
};
export const EXPERIMENTS_TO_TRACK = [
WORKFLOW_BUILDER_DEPRECATED_EXPERIMENT.name,
WORKFLOW_BUILDER_RELEASE_EXPERIMENT.name,
EXTRA_TEMPLATE_LINKS_EXPERIMENT.name,
TEMPLATE_ONBOARDING_EXPERIMENT.name,
NDV_UI_OVERHAUL_EXPERIMENT.name,

View File

@ -19,11 +19,7 @@ import { useSettingsStore } from '@/stores/settings.store';
import { defaultSettings } from '@/__tests__/defaults';
import merge from 'lodash/merge';
import { DEFAULT_POSTHOG_SETTINGS } from '@/stores/posthog.store.test';
import {
WORKFLOW_BUILDER_DEPRECATED_EXPERIMENT,
WORKFLOW_BUILDER_RELEASE_EXPERIMENT,
DEFAULT_NEW_WORKFLOW_NAME,
} from '@/constants';
import { DEFAULT_NEW_WORKFLOW_NAME } from '@/constants';
import { reactive } from 'vue';
import * as chatAPI from '@/api/ai';
import * as telemetryModule from '@/composables/useTelemetry';
@ -442,70 +438,16 @@ describe('AI Builder store', () => {
const settingsStore = useSettingsStore();
vi.spyOn(settingsStore, 'isAiBuilderEnabled', 'get').mockReturnValue(false);
vi.spyOn(posthogStore, 'getVariant').mockImplementation((experimentName) => {
if (experimentName === WORKFLOW_BUILDER_RELEASE_EXPERIMENT.name) {
return WORKFLOW_BUILDER_RELEASE_EXPERIMENT.variant;
}
return WORKFLOW_BUILDER_DEPRECATED_EXPERIMENT.control;
});
expect(builderStore.isAIBuilderEnabled).toBe(false);
});
it('should return true when license has aiBuilder and release experiment is set to variant', () => {
it('should return true when license has aiBuilder feature', () => {
const builderStore = useBuilderStore();
const settingsStore = useSettingsStore();
vi.spyOn(settingsStore, 'isAiBuilderEnabled', 'get').mockReturnValue(true);
vi.spyOn(posthogStore, 'getVariant').mockImplementation((experimentName) => {
if (experimentName === WORKFLOW_BUILDER_RELEASE_EXPERIMENT.name) {
return WORKFLOW_BUILDER_RELEASE_EXPERIMENT.variant;
}
return WORKFLOW_BUILDER_DEPRECATED_EXPERIMENT.control; // deprecated should be control
});
expect(builderStore.isAIBuilderEnabled).toBe(true);
});
it('should return true when license has aiBuilder and release experiment is control but deprecated experiment is variant', () => {
const builderStore = useBuilderStore();
const settingsStore = useSettingsStore();
vi.spyOn(settingsStore, 'isAiBuilderEnabled', 'get').mockReturnValue(true);
vi.spyOn(posthogStore, 'getVariant').mockImplementation((experimentName) => {
if (experimentName === WORKFLOW_BUILDER_RELEASE_EXPERIMENT.name) {
return WORKFLOW_BUILDER_RELEASE_EXPERIMENT.control;
}
return WORKFLOW_BUILDER_DEPRECATED_EXPERIMENT.variant;
});
expect(builderStore.isAIBuilderEnabled).toBe(true);
});
it('should return false when license has aiBuilder but both experiments are set to control', () => {
const builderStore = useBuilderStore();
const settingsStore = useSettingsStore();
vi.spyOn(settingsStore, 'isAiBuilderEnabled', 'get').mockReturnValue(true);
vi.spyOn(posthogStore, 'getVariant').mockImplementation((experimentName) => {
if (experimentName === WORKFLOW_BUILDER_RELEASE_EXPERIMENT.name) {
return WORKFLOW_BUILDER_RELEASE_EXPERIMENT.control;
}
return WORKFLOW_BUILDER_DEPRECATED_EXPERIMENT.control;
});
expect(builderStore.isAIBuilderEnabled).toBe(false);
});
it('should prioritize release experiment over deprecated experiment when license has aiBuilder', () => {
const builderStore = useBuilderStore();
const settingsStore = useSettingsStore();
vi.spyOn(settingsStore, 'isAiBuilderEnabled', 'get').mockReturnValue(true);
vi.spyOn(posthogStore, 'getVariant').mockImplementation((experimentName) => {
if (experimentName === WORKFLOW_BUILDER_RELEASE_EXPERIMENT.name) {
return WORKFLOW_BUILDER_RELEASE_EXPERIMENT.variant;
}
// Even if deprecated is control, release variant should win
return WORKFLOW_BUILDER_DEPRECATED_EXPERIMENT.control;
});
expect(builderStore.isAIBuilderEnabled).toBe(true);
});
});
@ -787,8 +729,6 @@ describe('AI Builder store', () => {
// Verify the API was called with correct parameters
expect(apiSpy).toHaveBeenCalled();
const callArgs = apiSpy.mock.calls[0];
expect(callArgs).toHaveLength(7); // Should have 7 arguments
const signal = callArgs[5]; // The 6th argument is the abort signal
expect(signal).toBeDefined();
expect(signal).toBeInstanceOf(AbortSignal);
@ -1229,92 +1169,6 @@ describe('AI Builder store', () => {
});
});
describe('useDeprecatedCredentials logic in sendChatMessage', () => {
it('should set useDeprecatedCredentials to true when release experiment is control and deprecated experiment is variant', () => {
const builderStore = useBuilderStore();
// Mock posthog to return control for release and variant for deprecated
vi.spyOn(posthogStore, 'getVariant').mockImplementation((experimentName) => {
if (experimentName === WORKFLOW_BUILDER_RELEASE_EXPERIMENT.name) {
return WORKFLOW_BUILDER_RELEASE_EXPERIMENT.control;
}
return WORKFLOW_BUILDER_DEPRECATED_EXPERIMENT.variant;
});
// Mock the API to capture the arguments
apiSpy.mockImplementationOnce(() => {});
builderStore.sendChatMessage({ text: 'test message' });
// Verify chatWithBuilder was called with useDeprecatedCredentials = true
expect(apiSpy).toHaveBeenCalledWith(
expect.anything(), // rootStore.restApiContext
expect.anything(), // payload
expect.anything(), // onMessage callback
expect.anything(), // onDone callback
expect.anything(), // onError callback
expect.anything(), // abort signal
true, // useDeprecatedCredentials
);
});
it('should set useDeprecatedCredentials to false when release experiment is variant', () => {
const builderStore = useBuilderStore();
// Mock posthog to return variant for release (regardless of deprecated)
vi.spyOn(posthogStore, 'getVariant').mockImplementation((experimentName) => {
if (experimentName === WORKFLOW_BUILDER_RELEASE_EXPERIMENT.name) {
return WORKFLOW_BUILDER_RELEASE_EXPERIMENT.variant;
}
return WORKFLOW_BUILDER_DEPRECATED_EXPERIMENT.variant;
});
// Mock the API to capture the arguments
apiSpy.mockImplementationOnce(() => {});
builderStore.sendChatMessage({ text: 'test message' });
// Verify chatWithBuilder was called with useDeprecatedCredentials = false
expect(apiSpy).toHaveBeenCalledWith(
expect.anything(), // rootStore.restApiContext
expect.anything(), // payload
expect.anything(), // onMessage callback
expect.anything(), // onDone callback
expect.anything(), // onError callback
expect.anything(), // abort signal
false, // useDeprecatedCredentials
);
});
it('should set useDeprecatedCredentials to false when both experiments are control', () => {
const builderStore = useBuilderStore();
// Mock posthog to return control for both experiments
vi.spyOn(posthogStore, 'getVariant').mockImplementation((experimentName) => {
if (experimentName === WORKFLOW_BUILDER_RELEASE_EXPERIMENT.name) {
return WORKFLOW_BUILDER_RELEASE_EXPERIMENT.control;
}
return WORKFLOW_BUILDER_DEPRECATED_EXPERIMENT.control;
});
// Mock the API to capture the arguments
apiSpy.mockImplementationOnce(() => {});
builderStore.sendChatMessage({ text: 'test message' });
// Verify chatWithBuilder was called with useDeprecatedCredentials = false
expect(apiSpy).toHaveBeenCalledWith(
expect.anything(), // rootStore.restApiContext
expect.anything(), // payload
expect.anything(), // onMessage callback
expect.anything(), // onDone callback
expect.anything(), // onError callback
expect.anything(), // abort signal
false, // useDeprecatedCredentials
);
});
});
describe('Credits management', () => {
it('should update builder credits correctly', () => {
const builderStore = useBuilderStore();
@ -1462,16 +1316,12 @@ describe('AI Builder store', () => {
mockGetBuilderCredits.mockClear();
});
it('should fetch and update credits when release experiment is variant', async () => {
it('should fetch and update credits when AI builder is enabled', async () => {
const builderStore = useBuilderStore();
const settingsStore = useSettingsStore();
// Mock posthog to return variant for release experiment
vi.spyOn(posthogStore, 'getVariant').mockImplementation((experimentName) => {
if (experimentName === WORKFLOW_BUILDER_RELEASE_EXPERIMENT.name) {
return WORKFLOW_BUILDER_RELEASE_EXPERIMENT.variant;
}
return WORKFLOW_BUILDER_DEPRECATED_EXPERIMENT.control;
});
// Mock AI builder as enabled
vi.spyOn(settingsStore, 'isAiBuilderEnabled', 'get').mockReturnValue(true);
// Mock API response
mockGetBuilderCredits.mockResolvedValueOnce({
@ -1486,16 +1336,12 @@ describe('AI Builder store', () => {
expect(builderStore.creditsRemaining).toBe(150);
});
it('should not fetch credits when release experiment is not variant', async () => {
it('should not fetch credits when AI builder is not enabled', async () => {
const builderStore = useBuilderStore();
const settingsStore = useSettingsStore();
// Mock posthog to return control for release experiment
vi.spyOn(posthogStore, 'getVariant').mockImplementation((experimentName) => {
if (experimentName === WORKFLOW_BUILDER_RELEASE_EXPERIMENT.name) {
return WORKFLOW_BUILDER_RELEASE_EXPERIMENT.control;
}
return WORKFLOW_BUILDER_DEPRECATED_EXPERIMENT.variant;
});
// Mock AI builder as disabled
vi.spyOn(settingsStore, 'isAiBuilderEnabled', 'get').mockReturnValue(false);
await builderStore.fetchBuilderCredits();
@ -1506,14 +1352,10 @@ describe('AI Builder store', () => {
it('should handle API errors gracefully', async () => {
const builderStore = useBuilderStore();
const settingsStore = useSettingsStore();
// Mock posthog to return variant for release experiment
vi.spyOn(posthogStore, 'getVariant').mockImplementation((experimentName) => {
if (experimentName === WORKFLOW_BUILDER_RELEASE_EXPERIMENT.name) {
return WORKFLOW_BUILDER_RELEASE_EXPERIMENT.variant;
}
return WORKFLOW_BUILDER_DEPRECATED_EXPERIMENT.control;
});
// Mock AI builder as enabled
vi.spyOn(settingsStore, 'isAiBuilderEnabled', 'get').mockReturnValue(true);
// Mock API to throw error
mockGetBuilderCredits.mockRejectedValueOnce(new Error('API error'));
@ -1529,14 +1371,10 @@ describe('AI Builder store', () => {
it('should call fetchBuilderCredits when opening chat', async () => {
const builderStore = useBuilderStore();
const chatPanelStore = useChatPanelStore();
const settingsStore = useSettingsStore();
// Mock posthog to return variant for release experiment
vi.spyOn(posthogStore, 'getVariant').mockImplementation((experimentName) => {
if (experimentName === WORKFLOW_BUILDER_RELEASE_EXPERIMENT.name) {
return WORKFLOW_BUILDER_RELEASE_EXPERIMENT.variant;
}
return WORKFLOW_BUILDER_DEPRECATED_EXPERIMENT.control;
});
// Mock AI builder as enabled
vi.spyOn(settingsStore, 'isAiBuilderEnabled', 'get').mockReturnValue(true);
// Mock API response
mockGetBuilderCredits.mockResolvedValueOnce({

View File

@ -1,10 +1,5 @@
import type { VIEWS } from '@/constants';
import {
DEFAULT_NEW_WORKFLOW_NAME,
WORKFLOW_BUILDER_DEPRECATED_EXPERIMENT,
WORKFLOW_BUILDER_RELEASE_EXPERIMENT,
PLACEHOLDER_EMPTY_WORKFLOW_ID,
} from '@/constants';
import { DEFAULT_NEW_WORKFLOW_NAME, PLACEHOLDER_EMPTY_WORKFLOW_ID } from '@/constants';
import { BUILDER_ENABLED_VIEWS } from './constants';
import { STORES } from '@n8n/stores';
import type { ChatUI } from '@n8n/design-system/types/assistant';
@ -16,7 +11,6 @@ import { useSettingsStore } from '@/stores/settings.store';
import { assert } from '@n8n/utils/assert';
import { useI18n } from '@n8n/i18n';
import { useTelemetry } from '@/composables/useTelemetry';
import { usePostHog } from '@/stores/posthog.store';
import { useWorkflowsStore } from '@/stores/workflows.store';
import { useBuilderMessages } from './composables/useBuilderMessages';
import { chatWithBuilder, getAiSessions, getBuilderCredits, getSessionsMetadata } from '@/api/ai';
@ -57,7 +51,6 @@ export const useBuilderStore = defineStore(STORES.BUILDER, () => {
const route = useRoute();
const locale = useI18n();
const telemetry = useTelemetry();
const posthogStore = usePostHog();
// Composables
const {
@ -81,23 +74,8 @@ export const useBuilderStore = defineStore(STORES.BUILDER, () => {
return firstUserMessage?.content;
});
const isAIBuilderEnabled = computed(() => {
// Check license first
if (!settings.isAiBuilderEnabled) {
return false;
}
const releaseExperimentVariant = posthogStore.getVariant(
WORKFLOW_BUILDER_RELEASE_EXPERIMENT.name,
);
if (releaseExperimentVariant === WORKFLOW_BUILDER_RELEASE_EXPERIMENT.variant) {
return true;
}
return (
posthogStore.getVariant(WORKFLOW_BUILDER_DEPRECATED_EXPERIMENT.name) ===
WORKFLOW_BUILDER_DEPRECATED_EXPERIMENT.variant
);
const isAIBuilderEnabled = computed((): boolean => {
return settings.isAiBuilderEnabled;
});
const toolMessages = computed(() => chatMessages.value.filter(isToolMessage));
@ -305,12 +283,6 @@ export const useBuilderStore = defineStore(STORES.BUILDER, () => {
streamingAbortController.value.abort();
}
const useDeprecatedCredentials =
posthogStore.getVariant(WORKFLOW_BUILDER_RELEASE_EXPERIMENT.name) !==
WORKFLOW_BUILDER_RELEASE_EXPERIMENT.variant &&
posthogStore.getVariant(WORKFLOW_BUILDER_DEPRECATED_EXPERIMENT.name) ===
WORKFLOW_BUILDER_DEPRECATED_EXPERIMENT.variant;
streamingAbortController.value = new AbortController();
try {
chatWithBuilder(
@ -335,7 +307,6 @@ export const useBuilderStore = defineStore(STORES.BUILDER, () => {
() => stopStreaming(),
(e) => handleServiceError(e, messageId, retry),
streamingAbortController.value?.signal,
useDeprecatedCredentials,
);
} catch (e: unknown) {
handleServiceError(e, messageId, retry);
@ -508,10 +479,7 @@ export const useBuilderStore = defineStore(STORES.BUILDER, () => {
}
async function fetchBuilderCredits() {
const releaseExperimentVariant = posthogStore.getVariant(
WORKFLOW_BUILDER_RELEASE_EXPERIMENT.name,
);
if (releaseExperimentVariant !== WORKFLOW_BUILDER_RELEASE_EXPERIMENT.variant) {
if (!isAIBuilderEnabled.value) {
return;
}