mirror of
https://github.com/n8n-io/n8n.git
synced 2026-06-05 02:59:27 +02:00
refactor(ai-builder): Remove retired AI builder experiment flags (no-changelog) (#31744)
This commit is contained in:
parent
a82384f90d
commit
ab741ed6db
|
|
@ -369,12 +369,7 @@ export function createMultiAgentWorkflowWithSubgraphs(config: MultiAgentSubgraph
|
|||
|
||||
// After discovery in plan mode, getNextPhaseFromLog returns 'builder'.
|
||||
// With builder removed, redirect back to discovery to retry planning.
|
||||
if (
|
||||
next === 'builder' &&
|
||||
featureFlags?.planMode === true &&
|
||||
state.mode === 'plan' &&
|
||||
!state.planOutput
|
||||
) {
|
||||
if (next === 'builder' && state.mode === 'plan' && !state.planOutput) {
|
||||
return { nextPhase: 'discovery', planDecision: null };
|
||||
}
|
||||
|
||||
|
|
@ -384,16 +379,10 @@ export function createMultiAgentWorkflowWithSubgraphs(config: MultiAgentSubgraph
|
|||
.addNode('check_state', (state) => {
|
||||
const action = determineStateAction(state, autoCompactThresholdTokens);
|
||||
|
||||
// In plan mode (without mergeAskBuild), skip the supervisor and
|
||||
// route directly to discovery (which contains the planner).
|
||||
// In plan mode (without mergeAskBuild), skip the supervisor and route directly to
|
||||
// discovery (which contains the planner).
|
||||
// Set nextPhase to 'discovery' so create_workflow_name can route correctly.
|
||||
if (
|
||||
action === 'continue' &&
|
||||
featureFlags?.planMode === true &&
|
||||
state.mode === 'plan' &&
|
||||
!state.planOutput &&
|
||||
!mergeAskBuild
|
||||
) {
|
||||
if (action === 'continue' && state.mode === 'plan' && !state.planOutput && !mergeAskBuild) {
|
||||
return { nextPhase: 'discovery' };
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,6 @@ export function formatExampleCategorizations(): string {
|
|||
|
||||
export interface DiscoveryPromptOptions {
|
||||
includeExamples: boolean;
|
||||
includeQuestions: boolean;
|
||||
}
|
||||
|
||||
const ROLE = `You are a Discovery Agent for n8n AI Workflow Builder.
|
||||
|
|
@ -52,21 +51,11 @@ const N8N_EXECUTION_MODEL = `n8n executes each node once per input item.
|
|||
|
||||
When a trigger or node outputs multiple items (e.g., Gmail returns 10 emails), every downstream node runs once for each item. Flow control nodes like Aggregate and Split Out change how items flow through the workflow by combining or expanding them.`;
|
||||
|
||||
const PROCESS = `1. Search for nodes matching the user's request using search_nodes tool
|
||||
2. Identify connection-changing parameters from input/output expressions (look for $parameter.X)
|
||||
3. Call submit_discovery_results with your nodesFound array`;
|
||||
|
||||
const PROCESS_WITH_QUESTIONS = `1. Search for nodes matching the user's request using search_nodes tool
|
||||
2. Identify connection-changing parameters from input/output expressions (look for $parameter.X)
|
||||
3. Assess: do you have enough information to build exactly what the user wants, or would you need to make assumptions about their intent? If assumptions are needed, ask clarifying questions using submit_questions (see clarifying_questions section)
|
||||
4. Call submit_discovery_results with your nodesFound array`;
|
||||
|
||||
const PROCESS_WITH_EXAMPLES = `1. Search for nodes matching the user's request using search_nodes tool
|
||||
2. Identify connection-changing parameters from input/output expressions (look for $parameter.X)
|
||||
3. Use get_documentation to retrieve best practices for relevant workflow techniques—this provides proven patterns that improve workflow quality
|
||||
4. Use get_workflow_examples to find real community workflows using mentioned services—these examples show how experienced users structure similar integrations
|
||||
5. Call submit_discovery_results with your nodesFound array`;
|
||||
|
||||
const PROCESS_WITH_EXAMPLES_AND_QUESTIONS = `1. Search for nodes matching the user's request using search_nodes tool
|
||||
2. Identify connection-changing parameters from input/output expressions (look for $parameter.X)
|
||||
3. Use get_documentation to retrieve best practices for relevant workflow techniques—this provides proven patterns that improve workflow quality
|
||||
|
|
@ -367,8 +356,7 @@ Guidelines:
|
|||
- Prioritize native nodes in your searches because they provide better UX and visual debugging than Code node alternatives`;
|
||||
|
||||
function generateToolCallRequirement(options: DiscoveryPromptOptions): string {
|
||||
const toolExamples = ['search_nodes'];
|
||||
if (options.includeQuestions) toolExamples.push('submit_questions');
|
||||
const toolExamples = ['search_nodes', 'submit_questions'];
|
||||
if (options.includeExamples) toolExamples.push('get_documentation', 'get_workflow_examples');
|
||||
|
||||
return `<output_requirement>
|
||||
|
|
@ -382,10 +370,8 @@ Do not output the results as text or XML.
|
|||
function generateAvailableToolsList(options: DiscoveryPromptOptions): string {
|
||||
const tools = [
|
||||
'- search_nodes: Find n8n nodes by keyword (returns name, version, inputs, outputs)',
|
||||
'- submit_questions: Ask clarifying questions when critical details are missing',
|
||||
];
|
||||
if (options.includeQuestions) {
|
||||
tools.push('- submit_questions: Ask clarifying questions when critical details are missing');
|
||||
}
|
||||
if (options.includeExamples) {
|
||||
tools.push(
|
||||
'- get_documentation: Retrieve best practices for workflow techniques to improve quality',
|
||||
|
|
@ -400,11 +386,7 @@ function generateAvailableToolsList(options: DiscoveryPromptOptions): string {
|
|||
}
|
||||
|
||||
function selectProcessSection(options: DiscoveryPromptOptions): string {
|
||||
if (options.includeExamples && options.includeQuestions)
|
||||
return PROCESS_WITH_EXAMPLES_AND_QUESTIONS;
|
||||
if (options.includeExamples) return PROCESS_WITH_EXAMPLES;
|
||||
if (options.includeQuestions) return PROCESS_WITH_QUESTIONS;
|
||||
return PROCESS;
|
||||
return options.includeExamples ? PROCESS_WITH_EXAMPLES_AND_QUESTIONS : PROCESS_WITH_QUESTIONS;
|
||||
}
|
||||
|
||||
export function buildDiscoveryPrompt(options: DiscoveryPromptOptions): string {
|
||||
|
|
@ -415,7 +397,7 @@ export function buildDiscoveryPrompt(options: DiscoveryPromptOptions): string {
|
|||
.section('available_tools', availableTools)
|
||||
.section('process', selectProcessSection(options))
|
||||
.section('tool_call_requirement', generateToolCallRequirement(options))
|
||||
.sectionIf(options.includeQuestions, 'clarifying_questions', CLARIFYING_QUESTIONS)
|
||||
.section('clarifying_questions', CLARIFYING_QUESTIONS)
|
||||
.section('n8n_execution_model', N8N_EXECUTION_MODEL)
|
||||
.section('baseline_flow_control', BASELINE_FLOW_CONTROL)
|
||||
.section('trigger_selection', TRIGGER_SELECTION)
|
||||
|
|
|
|||
|
|
@ -265,7 +265,6 @@ export class DiscoverySubgraph extends BaseSubgraph<
|
|||
private toolMap!: Map<string, StructuredTool>;
|
||||
private logger?: Logger;
|
||||
private parsedNodeTypes!: INodeTypeDescription[];
|
||||
private featureFlags?: BuilderFeatureFlags;
|
||||
|
||||
/** Mutable state for planner web_fetch hooks, updated before each planner invocation */
|
||||
private plannerWebFetchState: MutableWebFetchState = {
|
||||
|
|
@ -278,11 +277,9 @@ export class DiscoverySubgraph extends BaseSubgraph<
|
|||
create(config: DiscoverySubgraphConfig) {
|
||||
this.logger = config.logger;
|
||||
this.parsedNodeTypes = config.parsedNodeTypes;
|
||||
this.featureFlags = config.featureFlags;
|
||||
|
||||
// Check feature flags
|
||||
const includeExamples = config.featureFlags?.templateExamples === true;
|
||||
const includePlanMode = config.featureFlags?.planMode === true;
|
||||
const enableIntrospection = config.featureFlags?.enableIntrospection === true;
|
||||
|
||||
// Create security manager factories for web_fetch in each context
|
||||
|
|
@ -293,16 +290,11 @@ export class DiscoverySubgraph extends BaseSubgraph<
|
|||
const ssrf = config.ssrf ?? createPassthroughSsrfGuard();
|
||||
|
||||
// Create base tools - search_nodes provides all data needed for discovery
|
||||
const baseTools: StructuredTool[] = includePlanMode
|
||||
? [
|
||||
createNodeSearchTool(config.parsedNodeTypes).tool,
|
||||
submitQuestionsTool,
|
||||
createWebFetchTool(discoverySecurityFactory, ssrf).tool,
|
||||
]
|
||||
: [
|
||||
createNodeSearchTool(config.parsedNodeTypes).tool,
|
||||
createWebFetchTool(discoverySecurityFactory, ssrf).tool,
|
||||
];
|
||||
const baseTools: StructuredTool[] = [
|
||||
createNodeSearchTool(config.parsedNodeTypes).tool,
|
||||
submitQuestionsTool,
|
||||
createWebFetchTool(discoverySecurityFactory, ssrf).tool,
|
||||
];
|
||||
|
||||
// Conditionally add introspect tool if feature flag is enabled
|
||||
if (enableIntrospection) {
|
||||
|
|
@ -330,7 +322,6 @@ export class DiscoverySubgraph extends BaseSubgraph<
|
|||
// Generate prompt based on feature flags
|
||||
const discoveryPrompt = buildDiscoveryPrompt({
|
||||
includeExamples,
|
||||
includeQuestions: includePlanMode,
|
||||
});
|
||||
|
||||
// Create agent with tools bound (including submit tool)
|
||||
|
|
@ -418,7 +409,7 @@ export class DiscoverySubgraph extends BaseSubgraph<
|
|||
state: typeof DiscoverySubgraphState.State,
|
||||
runnableConfig?: RunnableConfig,
|
||||
) {
|
||||
if (!this.featureFlags?.planMode || state.mode !== 'plan' || state.planOutput) {
|
||||
if (state.mode !== 'plan' || state.planOutput) {
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
@ -459,7 +450,6 @@ export class DiscoverySubgraph extends BaseSubgraph<
|
|||
}
|
||||
|
||||
private shouldPlan(state: typeof DiscoverySubgraphState.State): 'planner' | typeof END {
|
||||
if (!this.featureFlags?.planMode) return END;
|
||||
if (state.mode !== 'plan') return END;
|
||||
return state.planOutput ? END : 'planner';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -114,7 +114,6 @@ describe('Plan Mode Discovery - Integration Tests', () => {
|
|||
parsedNodeTypes,
|
||||
llm,
|
||||
plannerLLM: llm,
|
||||
featureFlags: { planMode: true },
|
||||
checkpointer: new MemorySaver(),
|
||||
});
|
||||
}
|
||||
|
|
@ -317,7 +316,6 @@ describe('Plan Mode Discovery - Integration Tests', () => {
|
|||
parsedNodeTypes,
|
||||
llm,
|
||||
plannerLLM: llm,
|
||||
featureFlags: { planMode: true },
|
||||
});
|
||||
|
||||
console.log('Invoking in build mode...');
|
||||
|
|
|
|||
|
|
@ -356,7 +356,6 @@ describe('Question Quality - Integration Tests', () => {
|
|||
parsedNodeTypes,
|
||||
llm,
|
||||
plannerLLM: llm,
|
||||
featureFlags: { planMode: true },
|
||||
checkpointer: new MemorySaver(),
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -128,8 +128,6 @@ describe('WorkflowBuilderAgent', () => {
|
|||
mockPayload = {
|
||||
id: '12345',
|
||||
message: 'Create a workflow',
|
||||
// Test for plan mode as it's the only case when legacy multi-agent implementation is still in use
|
||||
featureFlags: { planMode: true },
|
||||
mode: 'plan',
|
||||
workflowContext: {
|
||||
currentWorkflow: { id: 'workflow-123' },
|
||||
|
|
@ -160,7 +158,6 @@ describe('WorkflowBuilderAgent', () => {
|
|||
const payload: ChatPayload = {
|
||||
id: '12345',
|
||||
message: validMessage,
|
||||
featureFlags: { planMode: true },
|
||||
mode: 'plan',
|
||||
};
|
||||
|
||||
|
|
@ -360,11 +357,10 @@ describe('WorkflowBuilderAgent', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('should route to multi-agent for initial plan request when planMode enabled', async () => {
|
||||
it('should route to multi-agent for initial plan request', async () => {
|
||||
const payload: ChatPayload = {
|
||||
id: '123',
|
||||
message: 'Create a weather alert workflow',
|
||||
featureFlags: { planMode: true },
|
||||
mode: 'plan',
|
||||
};
|
||||
|
||||
|
|
@ -390,7 +386,6 @@ describe('WorkflowBuilderAgent', () => {
|
|||
const payload: ChatPayload = {
|
||||
id: '123',
|
||||
message: 'Create a weather alert workflow',
|
||||
featureFlags: { planMode: true },
|
||||
resumeData: { action: 'approve' },
|
||||
resumeInterrupt: mockPlanInterrupt,
|
||||
};
|
||||
|
|
@ -416,7 +411,6 @@ describe('WorkflowBuilderAgent', () => {
|
|||
const payload: ChatPayload = {
|
||||
id: '123',
|
||||
message: 'Create a weather alert workflow',
|
||||
featureFlags: { planMode: true },
|
||||
resumeData: { action: 'modify', feedback: 'Add error handling' },
|
||||
resumeInterrupt: mockPlanInterrupt,
|
||||
};
|
||||
|
|
@ -442,7 +436,6 @@ describe('WorkflowBuilderAgent', () => {
|
|||
const payload: ChatPayload = {
|
||||
id: '123',
|
||||
message: 'Create a weather alert workflow',
|
||||
featureFlags: { planMode: true },
|
||||
resumeData: { action: 'reject' },
|
||||
resumeInterrupt: mockPlanInterrupt,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -111,7 +111,6 @@ export interface BuilderFeatureFlags {
|
|||
templateExamples?: boolean;
|
||||
/** Enable pin data generation in code builder (default: true). */
|
||||
pinData?: boolean;
|
||||
planMode?: boolean;
|
||||
/** Enable introspection tool for diagnostic data collection. Disabled by default. */
|
||||
enableIntrospection?: boolean;
|
||||
/** Enable merged ask/build experience with assistant subgraph (default: false). */
|
||||
|
|
@ -240,8 +239,6 @@ export class WorkflowBuilderAgent {
|
|||
externalCallbacks: Callbacks | undefined,
|
||||
historicalMessages: BaseMessage[] | undefined,
|
||||
) {
|
||||
const usePlanMode = payload.featureFlags?.planMode === true;
|
||||
|
||||
// web_fetch_approval resumes always go through multi-agent (where the interrupt lives)
|
||||
if (payload.resumeData && payload.resumeInterrupt?.type === 'web_fetch_approval') {
|
||||
this.logger?.debug('web_fetch_approval resume, routing to multi-agent system', {
|
||||
|
|
@ -292,7 +289,7 @@ export class WorkflowBuilderAgent {
|
|||
}
|
||||
|
||||
// Initial plan request: route to multi-agent for discovery + planning
|
||||
if (usePlanMode && payload.mode === 'plan') {
|
||||
if (payload.mode === 'plan') {
|
||||
this.logger?.debug('Plan mode with code builder, routing to multi-agent for planning', {
|
||||
userId,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -108,7 +108,6 @@ export class AiBuilderChatRequestDto extends Z.class({
|
|||
.object({
|
||||
templateExamples: z.boolean().optional(),
|
||||
pinData: z.boolean().optional(),
|
||||
planMode: z.boolean().optional(),
|
||||
mergeAskBuild: z.boolean().optional(),
|
||||
})
|
||||
.optional(),
|
||||
|
|
|
|||
|
|
@ -49,8 +49,6 @@ export const AI_BUILDER_TEMPLATE_EXAMPLES_EXPERIMENT = createExperiment(
|
|||
'056_ai_builder_template_examples',
|
||||
);
|
||||
|
||||
export const AI_BUILDER_PLAN_MODE_EXPERIMENT = createExperiment('073_builder_plan_mode');
|
||||
|
||||
export const AI_BUILDER_REVIEW_CHANGES_EXPERIMENT = createExperiment(
|
||||
'075_ai_builder_review_changes',
|
||||
);
|
||||
|
|
@ -67,10 +65,6 @@ export const CREDENTIALS_APP_SELECTION_EXPERIMENT = createExperiment(
|
|||
'065_credentials_app_selection',
|
||||
);
|
||||
|
||||
export const EMPTY_STATE_BUILDER_PROMPT_EXPERIMENT = createExperiment(
|
||||
'063_empty_state_builder_prompt',
|
||||
);
|
||||
|
||||
export const FOCUSED_NODES_EXPERIMENT = createExperiment('064_focused_nodes');
|
||||
|
||||
export const RESOURCE_CENTER_EXPERIMENT = createExperiment('063_resource_center_1');
|
||||
|
|
@ -130,7 +124,6 @@ export const EXPERIMENTS_TO_TRACK = [
|
|||
TEMPLATE_RECO_V2.name,
|
||||
READY_TO_RUN_V2_P3_EXPERIMENT.name,
|
||||
AI_BUILDER_TEMPLATE_EXAMPLES_EXPERIMENT.name,
|
||||
AI_BUILDER_PLAN_MODE_EXPERIMENT.name,
|
||||
TEMPLATE_SETUP_EXPERIENCE.name,
|
||||
RESOURCE_CENTER_EXPERIMENT.name,
|
||||
EXECUTION_LOGIC_V2_EXPERIMENT.name,
|
||||
|
|
@ -140,7 +133,6 @@ export const EXPERIMENTS_TO_TRACK = [
|
|||
EMPTY_STATE_EXPERIMENT.name,
|
||||
SETUP_PANEL.name,
|
||||
CODE_WORKFLOW_BUILDER_EXPERIMENT.name,
|
||||
EMPTY_STATE_BUILDER_PROMPT_EXPERIMENT.name,
|
||||
FOCUSED_NODES_EXPERIMENT.name,
|
||||
AI_BUILDER_REVIEW_CHANGES_EXPERIMENT.name,
|
||||
MERGE_ASK_BUILD_EXPERIMENT.name,
|
||||
|
|
|
|||
|
|
@ -126,7 +126,6 @@ export namespace ChatRequest {
|
|||
export interface BuilderFeatureFlags {
|
||||
templateExamples?: boolean;
|
||||
pinData?: boolean;
|
||||
planMode?: boolean;
|
||||
mergeAskBuild?: boolean;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,13 +19,13 @@ import { useSettingsStore } from '@/app/stores/settings.store';
|
|||
import { defaultSettings } from '@/__tests__/defaults';
|
||||
import { createTestNode } from '@/__tests__/mocks';
|
||||
import merge from 'lodash/merge';
|
||||
import { DEFAULT_POSTHOG_SETTINGS } from '@/app/stores/posthog.store.test';
|
||||
import { nextTick, reactive } from 'vue';
|
||||
import * as chatAPI from '@/features/ai/assistant/assistant.api';
|
||||
import * as telemetryModule from '@/app/composables/useTelemetry';
|
||||
import type { Telemetry } from '@/app/plugins/telemetry';
|
||||
import type { ChatUI } from '@n8n/design-system/types/assistant';
|
||||
import type { ChatRequest } from '@/features/ai/assistant/assistant.types';
|
||||
import type { FrontendSettings } from '@n8n/api-types';
|
||||
import type { INodeUi } from '@/Interface';
|
||||
import { mockedStore } from '@/__tests__/utils';
|
||||
import { useWorkflowsStore } from '@/app/stores/workflows.store';
|
||||
|
|
@ -36,7 +36,16 @@ import {
|
|||
useWorkflowDocumentStore,
|
||||
createWorkflowDocumentId,
|
||||
} from '@/app/stores/workflowDocument.store';
|
||||
import { AI_BUILDER_PLAN_MODE_EXPERIMENT } from '@/app/constants/experiments';
|
||||
|
||||
const DEFAULT_POSTHOG_SETTINGS: FrontendSettings['posthog'] = {
|
||||
enabled: true,
|
||||
apiHost: 'host',
|
||||
apiKey: 'key',
|
||||
autocapture: false,
|
||||
disableSessionRecording: true,
|
||||
debug: false,
|
||||
proxy: 'proxy',
|
||||
};
|
||||
|
||||
// Mock useI18n to return the keys instead of translations
|
||||
vi.mock('@n8n/i18n', () => ({
|
||||
|
|
@ -2227,16 +2236,7 @@ describe('AI Builder store', () => {
|
|||
});
|
||||
|
||||
describe('default plan mode based on canvas nodes', () => {
|
||||
function enablePlanModeExperiment() {
|
||||
vi.spyOn(posthogStore, 'getVariant').mockImplementation((experiment) =>
|
||||
experiment === AI_BUILDER_PLAN_MODE_EXPERIMENT.name
|
||||
? AI_BUILDER_PLAN_MODE_EXPERIMENT.variant
|
||||
: undefined,
|
||||
);
|
||||
}
|
||||
|
||||
it('should switch to plan mode when nodes become empty and plan mode is available', async () => {
|
||||
enablePlanModeExperiment();
|
||||
it('should switch to plan mode when nodes become empty', async () => {
|
||||
const builderStore = useBuilderStore();
|
||||
const workflowDocumentStore = useWorkflowDocumentStore(
|
||||
createWorkflowDocumentId(workflowsStore.workflowId),
|
||||
|
|
@ -2253,7 +2253,6 @@ describe('AI Builder store', () => {
|
|||
});
|
||||
|
||||
it('should switch to build mode when nodes are added', async () => {
|
||||
enablePlanModeExperiment();
|
||||
// Start with nodes so the watcher can observe changes
|
||||
const workflowDocumentStore = useWorkflowDocumentStore(
|
||||
createWorkflowDocumentId(workflowsStore.workflowId),
|
||||
|
|
@ -2274,18 +2273,7 @@ describe('AI Builder store', () => {
|
|||
expect(builderStore.builderMode).toBe('build');
|
||||
});
|
||||
|
||||
it('should stay in build mode when plan mode experiment is not enabled', async () => {
|
||||
const builderStore = useBuilderStore();
|
||||
|
||||
// Change workflowId to trigger the watcher (nodes stay empty)
|
||||
workflowsStore.setWorkflowId('different-workflow-id');
|
||||
await nextTick();
|
||||
|
||||
expect(builderStore.builderMode).toBe('build');
|
||||
});
|
||||
|
||||
it('should not change mode when chat has messages', async () => {
|
||||
enablePlanModeExperiment();
|
||||
const builderStore = useBuilderStore();
|
||||
const workflowDocumentStore = useWorkflowDocumentStore(
|
||||
createWorkflowDocumentId(workflowsStore.workflowId),
|
||||
|
|
@ -2307,7 +2295,6 @@ describe('AI Builder store', () => {
|
|||
});
|
||||
|
||||
it('should default to plan mode when workflowId changes with empty canvas', async () => {
|
||||
enablePlanModeExperiment();
|
||||
const builderStore = useBuilderStore();
|
||||
|
||||
// Simulate navigating to a new empty workflow
|
||||
|
|
@ -2322,7 +2309,6 @@ describe('AI Builder store', () => {
|
|||
});
|
||||
|
||||
it('should not switch to plan mode after restoreToVersion truncates messages', async () => {
|
||||
enablePlanModeExperiment();
|
||||
const builderStore = useBuilderStore();
|
||||
const workflowDocumentStore = useWorkflowDocumentStore(
|
||||
createWorkflowDocumentId(workflowsStore.workflowId),
|
||||
|
|
@ -3430,14 +3416,6 @@ describe('AI Builder store', () => {
|
|||
});
|
||||
|
||||
describe('Plan mode telemetry', () => {
|
||||
function enablePlanMode() {
|
||||
vi.spyOn(posthogStore, 'getVariant').mockImplementation((experiment) =>
|
||||
experiment === AI_BUILDER_PLAN_MODE_EXPERIMENT.name
|
||||
? AI_BUILDER_PLAN_MODE_EXPERIMENT.variant
|
||||
: undefined,
|
||||
);
|
||||
}
|
||||
|
||||
function addPlanMessageToChat(builderStore: ReturnType<typeof useBuilderStore>) {
|
||||
builderStore.chatMessages.push({
|
||||
role: 'assistant',
|
||||
|
|
@ -3452,7 +3430,6 @@ describe('AI Builder store', () => {
|
|||
describe('user_switched_builder_mode', () => {
|
||||
it('tracks journey event when switching to plan mode', () => {
|
||||
const builderStore = useBuilderStore();
|
||||
enablePlanMode();
|
||||
|
||||
track.mockClear();
|
||||
builderStore.setBuilderMode('plan');
|
||||
|
|
@ -3468,7 +3445,6 @@ describe('AI Builder store', () => {
|
|||
|
||||
it('tracks journey event when switching to build mode', () => {
|
||||
const builderStore = useBuilderStore();
|
||||
enablePlanMode();
|
||||
builderStore.setBuilderMode('plan');
|
||||
|
||||
track.mockClear();
|
||||
|
|
@ -3482,27 +3458,11 @@ describe('AI Builder store', () => {
|
|||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('does not track when plan mode is unavailable', () => {
|
||||
const builderStore = useBuilderStore();
|
||||
// Do NOT enable plan mode
|
||||
|
||||
track.mockClear();
|
||||
builderStore.setBuilderMode('plan');
|
||||
|
||||
expect(track).not.toHaveBeenCalledWith(
|
||||
'Workflow builder journey',
|
||||
expect.objectContaining({
|
||||
event_type: 'user_switched_builder_mode',
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('user_clicked_implement_plan', () => {
|
||||
it('tracks journey event when user approves plan', async () => {
|
||||
const builderStore = useBuilderStore();
|
||||
enablePlanMode();
|
||||
builderStore.setBuilderMode('plan');
|
||||
|
||||
// Set up interrupted state with a plan message
|
||||
|
|
@ -3527,7 +3487,6 @@ describe('AI Builder store', () => {
|
|||
describe('mode in User submitted builder message', () => {
|
||||
it('includes plan mode', async () => {
|
||||
const builderStore = useBuilderStore();
|
||||
enablePlanMode();
|
||||
builderStore.setBuilderMode('plan');
|
||||
|
||||
apiSpy.mockImplementationOnce(() => {});
|
||||
|
|
@ -3572,7 +3531,6 @@ describe('AI Builder store', () => {
|
|||
|
||||
it('includes plan mode on abort when in plan mode', async () => {
|
||||
const builderStore = useBuilderStore();
|
||||
enablePlanMode();
|
||||
builderStore.setBuilderMode('plan');
|
||||
|
||||
apiSpy.mockImplementationOnce(() => {});
|
||||
|
|
@ -3589,7 +3547,6 @@ describe('AI Builder store', () => {
|
|||
|
||||
it('includes plan_approved when plan was approved', async () => {
|
||||
const builderStore = useBuilderStore();
|
||||
enablePlanMode();
|
||||
builderStore.setBuilderMode('plan');
|
||||
|
||||
// Set up interrupted state with a plan message
|
||||
|
|
|
|||
|
|
@ -44,7 +44,6 @@ import { useWorkflowSaving } from '@/app/composables/useWorkflowSaving';
|
|||
import { useUIStore } from '@/app/stores/ui.store';
|
||||
import { useDocumentTitle } from '@/app/composables/useDocumentTitle';
|
||||
import { useBrowserNotifications } from '@/app/composables/useBrowserNotifications';
|
||||
import { AI_BUILDER_PLAN_MODE_EXPERIMENT } from '@/app/constants/experiments';
|
||||
import type { QuickReplyType } from '@n8n/api-types';
|
||||
import {
|
||||
isVersionCardMessage,
|
||||
|
|
@ -374,11 +373,6 @@ export const useBuilderStore = defineStore(STORES.BUILDER, () => {
|
|||
return { id: last.data.versionId, createdAt: last.data.createdAt };
|
||||
});
|
||||
|
||||
const isPlanModeAvailable = computed(() => {
|
||||
const variant = posthogStore.getVariant(AI_BUILDER_PLAN_MODE_EXPERIMENT.name);
|
||||
return variant === true || variant === AI_BUILDER_PLAN_MODE_EXPERIMENT.variant;
|
||||
});
|
||||
|
||||
/**
|
||||
* Finds the last interrupt message (questions or plan) by searching backwards.
|
||||
* This is more robust than checking only the last message, because error messages
|
||||
|
|
@ -454,7 +448,6 @@ export const useBuilderStore = defineStore(STORES.BUILDER, () => {
|
|||
}
|
||||
|
||||
function setBuilderMode(mode: 'build' | 'plan') {
|
||||
if (mode === 'plan' && !isPlanModeAvailable.value) return;
|
||||
builderMode.value = mode;
|
||||
trackWorkflowBuilderJourney('user_switched_builder_mode', { mode });
|
||||
}
|
||||
|
|
@ -1008,7 +1001,6 @@ export const useBuilderStore = defineStore(STORES.BUILDER, () => {
|
|||
executionData: executionResult,
|
||||
nodesForSchema: Object.keys(workflowDocumentStore.value.nodesByName),
|
||||
mode: modeForPayload,
|
||||
isPlanModeEnabled: isPlanModeAvailable.value,
|
||||
allowSendingParameterValues: settings.settings.ai.allowSendingParameterValues,
|
||||
});
|
||||
if (resumeData !== undefined) {
|
||||
|
|
@ -1521,7 +1513,6 @@ export const useBuilderStore = defineStore(STORES.BUILDER, () => {
|
|||
[() => workflowsStore.workflowId, () => workflowDocumentStore.value.allNodes.length],
|
||||
([, nodesCount]) => {
|
||||
if (chatMessages.value.length > 0) return;
|
||||
if (!isPlanModeAvailable.value) return;
|
||||
builderMode.value = nodesCount === 0 ? 'plan' : 'build';
|
||||
},
|
||||
);
|
||||
|
|
@ -1673,7 +1664,6 @@ export const useBuilderStore = defineStore(STORES.BUILDER, () => {
|
|||
isAIBuilderEnabled,
|
||||
isCodeBuilder,
|
||||
builderMode,
|
||||
isPlanModeAvailable,
|
||||
isInterrupted,
|
||||
hasPendingPlan,
|
||||
shouldDisableChatInput,
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ export async function createBuilderPayload(
|
|||
workflow?: IWorkflowDb;
|
||||
nodesForSchema?: string[];
|
||||
mode?: 'build' | 'plan';
|
||||
isPlanModeEnabled?: boolean;
|
||||
allowSendingParameterValues?: boolean;
|
||||
},
|
||||
): Promise<ChatRequest.UserChatMessage> {
|
||||
|
|
@ -86,7 +85,6 @@ export async function createBuilderPayload(
|
|||
posthogStore.getVariant(AI_BUILDER_TEMPLATE_EXAMPLES_EXPERIMENT.name) ===
|
||||
AI_BUILDER_TEMPLATE_EXAMPLES_EXPERIMENT.variant,
|
||||
pinData: isPinDataEnabled,
|
||||
planMode: options.isPlanModeEnabled ?? false,
|
||||
mergeAskBuild: posthogStore.isFeatureEnabled(MERGE_ASK_BUILD_EXPERIMENT.name),
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -869,7 +869,7 @@ defineExpose({
|
|||
@upgrade-click="onUpgradeClick"
|
||||
@vue:mounted="registerFocus(() => suggestionsInputRef?.focusInput())"
|
||||
>
|
||||
<template v-if="builderStore.isPlanModeAvailable" #right-actions>
|
||||
<template #right-actions>
|
||||
<PlanModeSelector
|
||||
:model-value="builderStore.builderMode"
|
||||
@update:model-value="builderStore.setBuilderMode"
|
||||
|
|
@ -907,7 +907,7 @@ defineExpose({
|
|||
@stop="builderStore.abortStreaming"
|
||||
@upgrade-click="() => goToUpgrade('ai-builder-sidebar', 'upgrade-builder')"
|
||||
>
|
||||
<template v-if="builderStore.isPlanModeAvailable" #right-actions>
|
||||
<template #right-actions>
|
||||
<PlanModeSelector
|
||||
:model-value="builderStore.builderMode"
|
||||
@update:model-value="builderStore.setBuilderMode"
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user