chore(editor): Cleanup pre-built agents code (no-changelog) (#23876)

This commit is contained in:
Svetoslav Dekov
2026-01-08 18:12:38 +02:00
committed by GitHub
parent 98abeb5319
commit 8578f309ec
39 changed files with 27 additions and 2545 deletions
@@ -319,20 +319,6 @@ export class AgentV1 implements INodeType {
},
},
},
{
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased
displayName: 'Get started faster with our',
name: 'preBuiltAgentsCallout',
type: 'callout',
typeOptions: {
calloutAction: {
label: 'pre-built agents',
icon: 'bot',
type: 'openPreBuiltAgentsCollection',
},
},
default: '',
},
{
displayName:
"This node is using Agent that has been deprecated. Please switch to using 'Tools Agent' instead.",
@@ -44,20 +44,6 @@ export class AgentV2 implements INodeType {
type: 'callout',
default: '',
},
{
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased
displayName: 'Get started faster with our',
name: 'preBuiltAgentsCallout',
type: 'callout',
typeOptions: {
calloutAction: {
label: 'pre-built agents',
icon: 'bot',
type: 'openPreBuiltAgentsCollection',
},
},
default: '',
},
promptTypeOptionsDeprecated,
{
...textFromGuardrailsNode,
@@ -1656,8 +1656,6 @@
"nodeCreator.nodeItem.aiIconTitle": "LangChain AI Node",
"nodeCreator.nodeItem.deprecated": "Deprecated",
"nodeCreator.nodeItem.beta": "Beta",
"nodeCreator.preBuiltAgents.title": "Pre-built agents",
"nodeCreator.preBuiltAgents.description": "Get started faster with ready to go agents",
"nodeCredentials.createNew": "Create new credential",
"nodeCredentials.createNew.permissionDenied": "Your current role does not allow you to create credentials",
"nodeCredentials.credentialFor": "Credential for {credentialType}",
@@ -3367,7 +3365,6 @@
"workflows.empty.learnN8n": "Learn n8n",
"workflows.empty.button.disabled.tooltip": "Your current role in the project does not allow you to create workflows",
"workflows.empty.easyAI": "Test a simple AI Agent example",
"workflows.empty.preBuiltAgents": "Try a pre-built agent",
"workflows.empty.shared-with-me": "No {resource} has been shared with you",
"workflows.empty.shared-with-me.link": "<a href=\"#\">Back to Personal</a>",
"workflows.empty.readyToRun": "Try an AI workflow",
@@ -3421,8 +3418,6 @@
"workflows.readyToRunWorkflows.error": "Error loading n8n collection. Please try again later.",
"workflows.archivedOnly.hint": "Archived workflows are hidden in this view. {link}",
"workflows.archivedOnly.hint.link": "Update filters",
"workflows.preBuiltAgents.callout": "Get started faster with our",
"workflows.preBuiltAgents.linkText": "pre-built agents",
"workflowSelectorParameterInput.createNewSubworkflow.name": "My Sub-Workflow",
"workflows.publishModal.title": "Publish workflow",
"workflows.publishModal.noTriggerMessage": "This workflow has no trigger nodes that require publishing",
@@ -4197,9 +4192,6 @@
"workflowDiff.newWorkflow": "New workflow",
"workflowDiff.newWorkflow.database": "The workflow will be created in the database",
"workflowDiff.newWorkflow.remote": "The workflow will be created on remote",
"preBuiltAgentTemplates.title": "Pre-built agents",
"preBuiltAgentTemplates.tutorials": "Tutorial templates",
"preBuiltAgentTemplates.viewAllLink": "View all templates",
"commandBar.placeholder": "Type a command or search...",
"commandBar.noResults": "No results found",
"commandBar.sections.recent": "Recent",
@@ -24,7 +24,6 @@ import { useTelemetry } from '@/app/composables/useTelemetry';
import { useBugReporting } from '@/app/composables/useBugReporting';
import { usePageRedirectionHelper } from '@/app/composables/usePageRedirectionHelper';
import { useKeybindings } from '@/app/composables/useKeybindings';
import { useCalloutHelpers } from '@/app/composables/useCalloutHelpers';
import { useSidebarLayout } from '@/app/composables/useSidebarLayout';
import { useSettingsItems } from '@/app/composables/useSettingsItems';
import { usePersonalizedTemplatesV2Store } from '@/experiments/templateRecoV2/stores/templateRecoV2.store';
@@ -54,7 +53,6 @@ const router = useRouter();
const telemetry = useTelemetry();
const pageRedirectionHelper = usePageRedirectionHelper();
const { getReportingURL } = useBugReporting();
const calloutHelpers = useCalloutHelpers();
const { isCollapsed, sidebarWidth, onResizeStart, onResize, onResizeEnd, toggleCollapse } =
useSidebarLayout();
@@ -91,18 +89,6 @@ const mainMenuItems = computed<IMenuItem[]>(() => [
icon: 'cloud',
available: settingsStore.isCloudDeployment && hasPermission(['instanceOwner']),
},
{
// Link to in-app pre-built agent templates, available experiment is enabled
id: 'templates',
icon: 'package-open',
label: i18n.baseText('generic.templates'),
position: 'bottom',
available:
settingsStore.isTemplatesEnabled &&
calloutHelpers.isPreBuiltAgentsCalloutVisible.value &&
!isTemplatesExperimentEnabled.value,
route: { to: { name: VIEWS.PRE_BUILT_AGENT_TEMPLATES } },
},
{
// Link to personalized template modal, available when V2, V3 or data quality experiment is enabled
id: 'templates',
@@ -119,7 +105,6 @@ const mainMenuItems = computed<IMenuItem[]>(() => [
position: 'bottom',
available:
settingsStore.isTemplatesEnabled &&
!calloutHelpers.isPreBuiltAgentsCalloutVisible.value &&
templatesStore.hasCustomTemplatesHost &&
!isTemplatesExperimentEnabled.value,
route: { to: { name: VIEWS.TEMPLATES } },
@@ -132,7 +117,6 @@ const mainMenuItems = computed<IMenuItem[]>(() => [
position: 'bottom',
available:
settingsStore.isTemplatesEnabled &&
!calloutHelpers.isPreBuiltAgentsCalloutVisible.value &&
!templatesStore.hasCustomTemplatesHost &&
!isTemplatesExperimentEnabled.value,
link: {
@@ -17,7 +17,6 @@ import {
PROMPT_MFA_CODE_MODAL_KEY,
SETUP_CREDENTIALS_MODAL_KEY,
WHATS_NEW_MODAL_KEY,
PRE_BUILT_AGENTS_MODAL_KEY,
WORKFLOW_ACTIVATION_CONFLICTING_WEBHOOK_MODAL_KEY,
WORKFLOW_ACTIVE_MODAL_KEY,
WORKFLOW_DIFF_MODAL_KEY,
@@ -93,7 +92,6 @@ import MfaSetupModal from '@/features/core/auth/components/MfaSetupModal.vue';
import ModalRoot from '@/app/components/ModalRoot.vue';
import NpsSurvey from '@/app/components/NpsSurvey.vue';
import PersonalizationModal from '@/features/settings/users/components/PersonalizationModal.vue';
import PreBuiltAgentsModal from '@/app/components/PreBuiltAgentsModal.vue';
import ProjectMoveResourceModal from '@/features/collaboration/projects/components/ProjectMoveResourceModal.vue';
import EventDestinationSettingsModal from '@/features/integrations/logStreaming.ee/components/EventDestinationSettingsModal.vue';
import SetupWorkflowCredentialsModal from '@/features/workflows/templates/components/SetupWorkflowCredentialsModal.vue';
@@ -419,12 +417,6 @@ import CredentialResolverEditModal from '@/app/components/CredentialResolverEdit
</template>
</ModalRoot>
<ModalRoot :name="PRE_BUILT_AGENTS_MODAL_KEY">
<template #default="{ modalName, data }">
<PreBuiltAgentsModal :modal-name="modalName" :data="data" />
</template>
</ModalRoot>
<ModalRoot :name="VARIABLE_MODAL_KEY">
<template #default="{ data }: { data: { mode: 'new' | 'edit'; variable?: any } }">
<VariableModal :mode="data?.mode ?? 'new'" :variable="data?.variable" />
@@ -1,81 +0,0 @@
<script setup lang="ts">
import Modal from '@/app/components/Modal.vue';
import { useI18n } from '@n8n/i18n';
import { PRE_BUILT_AGENTS_MODAL_KEY } from '@/app/constants';
import { createEventBus } from '@n8n/utils/event-bus';
import { computed } from 'vue';
import { useCalloutHelpers } from '@/app/composables/useCalloutHelpers';
import type { INodeCreateElement } from '@/Interface';
import ItemsRenderer from '@/features/shared/nodeCreator/components/Renderers/ItemsRenderer.vue';
import { N8nHeading } from '@n8n/design-system';
const i18n = useI18n();
const modalBus = createEventBus();
const calloutHelpers = useCalloutHelpers();
const preBuiltAgents = computed<INodeCreateElement[]>(() =>
calloutHelpers.getPreBuiltAgentNodeCreatorItems(),
);
function onSelected(actionCreateElement: INodeCreateElement) {
if (actionCreateElement.type === 'openTemplate') {
calloutHelpers.openSampleWorkflowTemplate(actionCreateElement.properties.templateId, {
telemetry: {
source: 'modal',
},
});
}
}
</script>
<template>
<Modal
max-width="500px"
max-height="85vh"
:event-bus="modalBus"
:name="PRE_BUILT_AGENTS_MODAL_KEY"
:center="true"
:show-close="true"
:class="$style.modal"
>
<template #header>
<div :class="$style.header">
<N8nHeading size="xlarge">{{ i18n.baseText('workflows.empty.preBuiltAgents') }}</N8nHeading>
</div>
</template>
<template #content>
<div :class="$style.container">
<ItemsRenderer :elements="preBuiltAgents" :class="$style.items" @selected="onSelected" />
</div>
</template>
</Modal>
</template>
<style lang="scss" module>
.modal {
:global(.el-dialog__body) {
padding: 0;
padding-bottom: var(--spacing--sm);
}
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
padding-bottom: var(--spacing--sm);
}
.container {
display: flex;
flex-direction: column;
min-height: 100%;
}
.item {
margin-left: 0;
margin-right: 0;
padding-right: 0;
}
</style>
@@ -1,21 +1,13 @@
import { useCalloutHelpers } from '@/app/composables/useCalloutHelpers';
import { updateCurrentUserSettings } from '@n8n/rest-api-client/api/users';
import { createTestingPinia } from '@pinia/testing';
import {
PrebuiltAgentTemplates,
SampleTemplates,
} from '@/features/workflows/templates/utils/workflowSamples';
import { useNDVStore } from '@/features/ndv/shared/ndv.store';
import { mockedStore } from '@/__tests__/utils';
import { NODE_CREATOR_OPEN_SOURCES, VIEWS } from '@/app/constants';
import { useNodeCreatorStore } from '@/features/shared/nodeCreator/nodeCreator.store';
import { useViewStacks } from '@/features/shared/nodeCreator/composables/useViewStacks';
import { SampleTemplates } from '@/features/workflows/templates/utils/workflowSamples';
import { VIEWS } from '@/app/constants';
const mocks = vi.hoisted(() => ({
resolve: vi.fn(),
track: vi.fn(),
useRoute: vi.fn(() => ({ query: {}, params: {} })),
isVariantEnabled: vi.fn(() => false),
isCalloutDismissed: vi.fn(() => false),
setCalloutDismissed: vi.fn(),
restApiContext: vi.fn(() => ({})),
@@ -34,12 +26,6 @@ vi.mock('@/app/composables/useTelemetry', () => ({
useTelemetry: () => ({ track: mocks.track }),
}));
vi.mock('@/app/stores/posthog.store', () => ({
usePostHog: () => ({
isVariantEnabled: mocks.isVariantEnabled,
}),
}));
vi.mock('@/features/settings/users/users.store', () => ({
useUsersStore: () => ({
isCalloutDismissed: mocks.isCalloutDismissed,
@@ -73,17 +59,10 @@ vi.mock('@/features/collaboration/projects/projects.store', () => ({
}),
}));
let ndvStore: ReturnType<typeof mockedStore<typeof useNDVStore>>;
let nodeCreatorStore: ReturnType<typeof mockedStore<typeof useNodeCreatorStore>>;
let viewStacks: ReturnType<typeof mockedStore<typeof useViewStacks>>;
describe('useCalloutHelpers()', () => {
beforeEach(() => {
vi.clearAllMocks();
createTestingPinia();
ndvStore = mockedStore(useNDVStore);
nodeCreatorStore = mockedStore(useNodeCreatorStore);
viewStacks = mockedStore(useViewStacks);
});
describe('openSampleWorkflowTemplate()', () => {
@@ -193,90 +172,6 @@ describe('useCalloutHelpers()', () => {
},
});
});
it.each(Object.values(PrebuiltAgentTemplates))(
'opens pre-built agent template %s from NDV successfully',
(templateId) => {
vi.spyOn(window, 'open').mockImplementation(() => null);
mocks.resolve.mockReturnValue({ href: 'n8n.io' });
const { openSampleWorkflowTemplate } = useCalloutHelpers();
const nodeType = 'testNode';
openSampleWorkflowTemplate(templateId, {
telemetry: {
source: 'ndv',
nodeType,
},
});
expect(window.open).toHaveBeenCalledWith('n8n.io', '_blank');
expect(mocks.track).toHaveBeenCalledWith('User inserted pre-built Agent', {
source: 'ndv',
template: templateId,
node_type: nodeType,
section: null,
});
},
);
it.each(Object.values(PrebuiltAgentTemplates))(
'opens pre-built agent template %s from node creator successfully',
(templateId) => {
vi.spyOn(window, 'open').mockImplementation(() => null);
mocks.resolve.mockReturnValue({ href: 'n8n.io' });
const { openSampleWorkflowTemplate } = useCalloutHelpers();
const section = 'Test Section';
openSampleWorkflowTemplate(templateId, {
telemetry: {
source: 'nodeCreator',
section,
},
});
expect(window.open).toHaveBeenCalledWith('n8n.io', '_blank');
expect(mocks.track).toHaveBeenCalledWith('User inserted pre-built Agent', {
source: 'nodeCreator',
template: templateId,
node_type: null,
section,
});
},
);
});
describe('openPreBuiltAgentsCollection', () => {
it('opens pre-built agents collection successfully', async () => {
vi.spyOn(window, 'open').mockImplementation(() => null);
mocks.resolve.mockReturnValue({ href: 'n8n.io' });
const { openPreBuiltAgentsCollection } = useCalloutHelpers();
await openPreBuiltAgentsCollection({
telemetry: {
source: 'ndv',
nodeType: 'testNode',
section: 'testSection',
},
});
expect(ndvStore.unsetActiveNodeName).toHaveBeenCalled();
expect(nodeCreatorStore.setNodeCreatorState).toHaveBeenCalledWith({
source: NODE_CREATOR_OPEN_SOURCES.TEMPLATES_CALLOUT,
createNodeActive: true,
});
expect(viewStacks.pushViewStack).toHaveBeenCalledWith(
expect.objectContaining({ title: 'Pre-built agents' }),
{ resetStacks: false },
);
expect(mocks.track).toHaveBeenCalledWith('User opened pre-built Agents collection', {
source: 'ndv',
node_type: 'testNode',
section: 'testSection',
});
});
});
describe('isRagStarterCalloutVisible', () => {
@@ -305,22 +200,6 @@ describe('useCalloutHelpers()', () => {
});
});
describe('isPreBuiltAgentsCalloutVisible', () => {
it('should be false with experiment disabled', () => {
mocks.isVariantEnabled.mockReturnValueOnce(false);
const { isPreBuiltAgentsCalloutVisible } = useCalloutHelpers();
expect(isPreBuiltAgentsCalloutVisible.value).toBe(false);
});
it('should be true with experiment enabled', () => {
mocks.isVariantEnabled.mockReturnValueOnce(true);
const { isPreBuiltAgentsCalloutVisible } = useCalloutHelpers();
expect(isPreBuiltAgentsCalloutVisible.value).toBe(true);
});
});
describe('isCalloutDismissed()', () => {
it('should return false if callout is not dismissed', () => {
const { isCalloutDismissed } = useCalloutHelpers();
@@ -1,51 +1,31 @@
import { computed, nextTick } from 'vue';
import { computed } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { useTelemetry } from '@/app/composables/useTelemetry';
import { useRootStore } from '@n8n/stores/useRootStore';
import { useI18n } from '@n8n/i18n';
import { useUsersStore } from '@/features/settings/users/users.store';
import { useWorkflowsStore } from '@/app/stores/workflows.store';
import { usePostHog } from '@/app/stores/posthog.store';
import { useNDVStore } from '@/features/ndv/shared/ndv.store';
import { useNodeCreatorStore } from '@/features/shared/nodeCreator/nodeCreator.store';
import { useNodeTypesStore } from '@/app/stores/nodeTypes.store';
import { useViewStacks } from '@/features/shared/nodeCreator/composables/useViewStacks';
import { updateCurrentUserSettings } from '@n8n/rest-api-client/api/users';
import { VIEWS } from '@/app/constants';
import {
NODE_CREATOR_OPEN_SOURCES,
PRE_BUILT_AGENTS_EXPERIMENT,
PRE_BUILT_AGENTS_MODAL_KEY,
REGULAR_NODE_CREATOR_VIEW,
VIEWS,
} from '@/app/constants';
import {
getPrebuiltAgents,
getRagStarterWorkflowJson,
getSampleWorkflowByTemplateId,
getTutorialTemplates,
isPrebuiltAgentTemplateId,
isTutorialTemplateId,
SampleTemplates,
} from '@/features/workflows/templates/utils/workflowSamples';
import type { INodeCreateElement, OpenTemplateElement } from '@/Interface';
import { useUIStore } from '@/app/stores/ui.store';
import type { OpenTemplateElement } from '@/Interface';
import { useProjectsStore } from '@/features/collaboration/projects/projects.store';
export function useCalloutHelpers() {
const route = useRoute();
const router = useRouter();
const telemetry = useTelemetry();
const postHog = usePostHog();
const i18n = useI18n();
const rootStore = useRootStore();
const workflowsStore = useWorkflowsStore();
const usersStore = useUsersStore();
const ndvStore = useNDVStore();
const nodeCreatorStore = useNodeCreatorStore();
const viewStacks = useViewStacks();
const nodeTypesStore = useNodeTypesStore();
const uiStore = useUIStore();
const projectsStore = useProjectsStore();
const isRagStarterCalloutVisible = computed(() => {
@@ -63,29 +43,6 @@ export function useCalloutHelpers() {
return true;
});
const getPreBuiltAgentNodeCreatorItems = (): OpenTemplateElement[] => {
const templates = getPrebuiltAgents();
return templates.map((template) => {
return {
key: template.template.meta.templateId,
type: 'openTemplate',
properties: {
templateId: template.template.meta.templateId,
title: template.name,
description: template.description,
nodes: template.nodes.flatMap((node) => {
const nodeType = nodeTypesStore.getNodeType(node.name, node.version);
if (!nodeType) {
return [];
}
return nodeType;
}),
},
};
});
};
const getTutorialTemplatesNodeCreatorItems = (): OpenTemplateElement[] => {
const templates = getTutorialTemplates();
@@ -109,61 +66,6 @@ export function useCalloutHelpers() {
});
};
const openPreBuiltAgentsModal = async (source: 'workflowsEmptyState' | 'workflowsList') => {
telemetry.track('User opened pre-built Agents collection', {
source,
node_type: null,
section: null,
});
await nodeTypesStore.loadNodeTypesIfNotLoaded();
uiStore.openModal(PRE_BUILT_AGENTS_MODAL_KEY);
};
const openPreBuiltAgentsCollection = async (options: {
telemetry: {
source: 'ndv' | 'nodeCreator';
nodeType?: string;
section?: string;
};
resetStacks?: boolean;
}) => {
telemetry.track('User opened pre-built Agents collection', {
source: options.telemetry.source,
node_type: options.telemetry.nodeType ?? null,
section: options.telemetry.section ?? null,
});
await nodeTypesStore.loadNodeTypesIfNotLoaded();
const items: INodeCreateElement[] = getPreBuiltAgentNodeCreatorItems();
ndvStore.unsetActiveNodeName();
nodeCreatorStore.setNodeCreatorState({
source: NODE_CREATOR_OPEN_SOURCES.TEMPLATES_CALLOUT,
createNodeActive: true,
nodeCreatorView: undefined,
connectionType: undefined,
});
await nextTick();
viewStacks.pushViewStack(
{
title: i18n.baseText('nodeCreator.preBuiltAgents.title'),
rootView: REGULAR_NODE_CREATOR_VIEW,
activeIndex: 0,
transitionDirection: 'in',
hasSearch: false,
preventBack: false,
items,
baselineItems: items,
mode: 'nodes',
hideActions: false,
},
{ resetStacks: options.resetStacks ?? false },
);
};
const openSampleWorkflowTemplate = (
templateId: string,
options: {
@@ -178,13 +80,6 @@ export function useCalloutHelpers() {
telemetry.track('User clicked on RAG callout', {
node_type: options.telemetry.nodeType ?? null,
});
} else if (isPrebuiltAgentTemplateId(templateId)) {
telemetry.track('User inserted pre-built Agent', {
template: templateId,
source: options.telemetry.source,
node_type: options.telemetry.nodeType ?? null,
section: options.telemetry.section ?? null,
});
} else if (isTutorialTemplateId(templateId)) {
telemetry.track('User inserted tutorial template', {
template: templateId,
@@ -212,17 +107,6 @@ export function useCalloutHelpers() {
window.open(href, '_blank');
};
const isPreBuiltAgentsExperimentEnabled = computed(() => {
return postHog.isVariantEnabled(
PRE_BUILT_AGENTS_EXPERIMENT.name,
PRE_BUILT_AGENTS_EXPERIMENT.variant,
);
});
const isPreBuiltAgentsCalloutVisible = computed(() => {
return isPreBuiltAgentsExperimentEnabled.value;
});
const isCalloutDismissed = (callout: string) => {
return usersStore.isCalloutDismissed(callout);
};
@@ -240,12 +124,8 @@ export function useCalloutHelpers() {
return {
openSampleWorkflowTemplate,
openPreBuiltAgentsModal,
openPreBuiltAgentsCollection,
getPreBuiltAgentNodeCreatorItems,
getTutorialTemplatesNodeCreatorItems,
isRagStarterCalloutVisible,
isPreBuiltAgentsCalloutVisible,
isCalloutDismissed,
dismissCallout,
};
@@ -17,7 +17,6 @@ import { useWorkflowsStore } from '@/app/stores/workflows.store';
import { useBuilderStore } from '@/features/ai/assistant/builder.store';
import {
SampleTemplates,
isPrebuiltAgentTemplateId,
isTutorialTemplateId,
} from '@/features/workflows/templates/utils/workflowSamples';
import {
@@ -102,11 +101,6 @@ export async function executionFinished(
} else {
readyToRunStore.trackExecuteAiWorkflow(data.status);
}
} else if (isPrebuiltAgentTemplateId(templateId)) {
telemetry.track('User executed pre-built Agent', {
template: templateId,
status: data.status,
});
} else if (isTutorialTemplateId(templateId)) {
telemetry.track('User executed tutorial template', {
template: templateId,
@@ -31,12 +31,6 @@ export const BATCH_11AUG_EXPERIMENT = {
variantReadyToRun3: 'variant-ready-to-run-workflows_v3',
};
export const PRE_BUILT_AGENTS_EXPERIMENT = {
name: '038_pre_built_agents',
control: 'control',
variant: 'variant',
};
export const TEMPLATE_RECO_V2 = {
name: '039_template_onboarding_v2',
control: 'control',
@@ -105,7 +99,6 @@ export const EXPERIMENTS_TO_TRACK = [
EXTRA_TEMPLATE_LINKS_EXPERIMENT.name,
TEMPLATE_ONBOARDING_EXPERIMENT.name,
BATCH_11AUG_EXPERIMENT.name,
PRE_BUILT_AGENTS_EXPERIMENT.name,
TEMPLATE_RECO_V2.name,
TEMPLATES_DATA_QUALITY_EXPERIMENT.name,
READY_TO_RUN_V2_P3_EXPERIMENT.name,
@@ -31,7 +31,6 @@ export const STOP_MANY_EXECUTIONS_MODAL_KEY = 'stopManyExecutions';
export const WORKFLOW_EXTRACTION_NAME_MODAL_KEY = 'workflowExtractionName';
export const WHATS_NEW_MODAL_KEY = 'whatsNew';
export const WORKFLOW_DIFF_MODAL_KEY = 'workflowDiff';
export const PRE_BUILT_AGENTS_MODAL_KEY = 'preBuiltAgents';
export const EXPERIMENT_TEMPLATE_RECO_V2_KEY = 'templateRecoV2';
export const EXPERIMENT_TEMPLATE_RECO_V3_KEY = 'templateRecoV3';
export const EXPERIMENT_TEMPLATES_DATA_QUALITY_KEY = 'templatesDataQuality';
@@ -60,7 +60,6 @@ export const enum VIEWS {
SHARED_CREDENTIALS = 'SharedCredentials',
ENTITY_NOT_FOUND = 'EntityNotFound',
ENTITY_UNAUTHORIZED = 'EntityUnAuthorized',
PRE_BUILT_AGENT_TEMPLATES = 'PreBuiltAgentTemplates',
OAUTH_CONSENT = 'OAuthConsent',
MIGRATION_REPORT = 'MigrationReport',
MIGRATION_RULE_REPORT = 'MigrationRuleReport',
@@ -57,7 +57,6 @@ export const AI_UNCATEGORIZED_CATEGORY = 'Miscellaneous';
export const AI_CODE_TOOL_LANGCHAIN_NODE_TYPE = '@n8n/n8n-nodes-langchain.toolCode';
export const AI_WORKFLOW_TOOL_LANGCHAIN_NODE_TYPE = '@n8n/n8n-nodes-langchain.toolWorkflow';
export const REQUEST_NODE_FORM_URL = 'https://n8n-community.typeform.com/to/K1fBVTZ3';
export const PRE_BUILT_AGENTS_COLLECTION = 'pre-built-agents-collection';
export const RECOMMENDED_NODES: string[] = [DATA_TABLE_NODE_TYPE, DATA_TABLE_TOOL_NODE_TYPE];
export const BETA_NODES: string[] = [];
@@ -20,7 +20,6 @@ import { initializeAuthenticatedFeatures, initializeCore } from '@/app/init';
import { tryToParseNumber } from '@/app/utils/typesUtils';
import { projectsRoutes } from '@/features/collaboration/projects/projects.routes';
import { MfaRequiredError } from '@n8n/rest-api-client';
import { useCalloutHelpers } from '@/app/composables/useCalloutHelpers';
import { useRecentResources } from '@/features/shared/commandBar/composables/useRecentResources';
import { usePostHog } from '@/app/stores/posthog.store';
import { TEMPLATE_SETUP_EXPERIENCE } from '@/app/constants/experiments';
@@ -88,8 +87,6 @@ const TestRunDetailView = async () =>
await import('@/features/ai/evaluation.ee/views/TestRunDetailView.vue');
const EvaluationRootView = async () =>
await import('@/features/ai/evaluation.ee/views/EvaluationsRootView.vue');
const PrebuiltAgentTemplatesView = async () =>
await import('@/app/views/PrebuiltAgentTemplatesView.vue');
const MigrationReportView = async () =>
await import('@/features/settings/migrationReport/MigrationRules.vue');
@@ -134,26 +131,6 @@ export const routes: RouteRecordRaw[] = [
middleware: ['authenticated'],
},
},
{
path: '/templates/agents',
name: VIEWS.PRE_BUILT_AGENT_TEMPLATES,
component: PrebuiltAgentTemplatesView,
meta: {
templatesEnabled: true,
getRedirect: getTemplatesRedirect,
middleware: ['authenticated'],
},
beforeEnter: (_to, _from, next) => {
const calloutHelpers = useCalloutHelpers();
const templatesStore = useTemplatesStore();
if (!calloutHelpers.isPreBuiltAgentsCalloutVisible.value) {
window.location.href = templatesStore.websiteTemplateRepositoryURL;
} else {
next();
}
},
},
// Following two routes are kept in-app:
// Single workflow view, used when a custom template host is set
// Also, reachable directly from this URL
@@ -26,7 +26,6 @@ import {
LOCAL_STORAGE_THEME,
WHATS_NEW_MODAL_KEY,
WORKFLOW_DIFF_MODAL_KEY,
PRE_BUILT_AGENTS_MODAL_KEY,
EXPERIMENT_TEMPLATE_RECO_V2_KEY,
CONFIRM_PASSWORD_MODAL_KEY,
EXPERIMENT_TEMPLATE_RECO_V3_KEY,
@@ -150,7 +149,6 @@ export const useUIStore = defineStore(STORES.UI, () => {
PROJECT_MOVE_RESOURCE_MODAL,
NEW_ASSISTANT_SESSION_MODAL,
IMPORT_WORKFLOW_URL_MODAL_KEY,
PRE_BUILT_AGENTS_MODAL_KEY,
WORKFLOW_DIFF_MODAL_KEY,
EXPERIMENT_TEMPLATE_RECO_V3_KEY,
VARIABLE_MODAL_KEY,
@@ -1,175 +0,0 @@
<script setup lang="ts">
import { VIEWS } from '@/app/constants';
import { useI18n } from '@n8n/i18n';
import { useCalloutHelpers } from '@/app/composables/useCalloutHelpers';
import { computed, onMounted } from 'vue';
import type { OpenTemplateElement } from '@/Interface';
import { useNodeTypesStore } from '@/app/stores/nodeTypes.store';
import NodeIcon from '@/app/components/NodeIcon.vue';
import { N8nCard, N8nHeading, N8nLink, N8nNodeCreatorNode } from '@n8n/design-system';
import PageViewLayout from '@/app/components/layouts/PageViewLayout.vue';
const nodeTypesStore = useNodeTypesStore();
const i18n = useI18n();
const calloutHelpers = useCalloutHelpers();
const preBuiltAgents = computed<OpenTemplateElement[]>(() =>
calloutHelpers.getPreBuiltAgentNodeCreatorItems(),
);
const tutorials = computed<OpenTemplateElement[]>(() =>
calloutHelpers.getTutorialTemplatesNodeCreatorItems(),
);
const openTemplate = (templateId: string) => {
calloutHelpers.openSampleWorkflowTemplate(templateId, {
telemetry: {
source: 'templates',
},
});
};
onMounted(async () => {
await nodeTypesStore.loadNodeTypesIfNotLoaded();
});
</script>
<template>
<PageViewLayout>
<div :class="$style.content">
<section :class="$style.section">
<div :class="$style.header">
<N8nHeading tag="h2" bold size="xlarge">
{{ i18n.baseText('preBuiltAgentTemplates.title') }}
</N8nHeading>
<N8nLink :to="{ name: VIEWS.TEMPLATES }" underline bold>
{{ i18n.baseText('preBuiltAgentTemplates.viewAllLink') }}
</N8nLink>
</div>
<div :class="$style.grid">
<N8nCard
v-for="template in preBuiltAgents"
:key="template.key"
:class="$style.card"
@click="openTemplate(template.properties.templateId)"
>
<N8nNodeCreatorNode
:class="$style.templateLink"
:title="template.properties.title"
:description="template.properties.description"
:tag="template.properties.tag"
:show-action-arrow="true"
:is-trigger="false"
:hide-node-icon="true"
>
<template v-if="template.properties.nodes" #extraDetails>
<NodeIcon
v-for="node in template.properties.nodes"
:key="node.name"
:node-type="node"
:size="16"
:show-tooltip="true"
/>
</template>
</N8nNodeCreatorNode>
</N8nCard>
</div>
</section>
<section :class="$style.section">
<N8nHeading tag="h2" bold size="xlarge">
{{ i18n.baseText('preBuiltAgentTemplates.tutorials') }}
</N8nHeading>
<div :class="$style.tutorials">
<N8nCard
v-for="template in tutorials"
:key="template.key"
:class="$style.card"
@click="openTemplate(template.properties.templateId)"
>
<N8nNodeCreatorNode
:class="$style.templateLink"
:title="template.properties.title"
:description="template.properties.description"
:tag="template.properties.tag"
:show-action-arrow="true"
:is-trigger="false"
:hide-node-icon="true"
>
<template v-if="template.properties.nodes" #extraDetails>
<NodeIcon
v-for="node in template.properties.nodes"
:key="node.name"
:node-type="node"
:size="16"
:show-tooltip="true"
/>
</template>
</N8nNodeCreatorNode>
</N8nCard>
</div>
</section>
<N8nLink :to="{ name: VIEWS.TEMPLATES }" underline bold>
{{ i18n.baseText('preBuiltAgentTemplates.viewAllLink') }}
</N8nLink>
</div>
</PageViewLayout>
</template>
<style lang="scss" module>
.content {
display: flex;
flex-direction: column;
justify-content: flex-start;
gap: var(--spacing--md);
padding-bottom: var(--spacing--lg);
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
}
.section {
display: flex;
flex-direction: column;
gap: var(--spacing--sm);
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: var(--spacing--sm);
width: 100%;
box-sizing: border-box;
align-items: start;
align-content: start;
grid-auto-rows: auto;
margin-bottom: var(--spacing--sm);
}
.card {
margin: 0;
display: flex;
flex-direction: column;
align-self: start;
}
.tutorials {
display: flex;
flex-direction: column;
width: 100%;
gap: var(--spacing--sm);
margin-bottom: var(--spacing--sm);
}
.templateLink {
padding: 0;
}
</style>
@@ -17,7 +17,6 @@ import { useMessage } from '@/app/composables/useMessage';
import { useProjectPages } from '@/features/collaboration/projects/composables/useProjectPages';
import { useTelemetry } from '@/app/composables/useTelemetry';
import { useToast } from '@/app/composables/useToast';
import { useCalloutHelpers } from '@/app/composables/useCalloutHelpers';
import {
DEFAULT_WORKFLOW_PAGE_SIZE,
EnterpriseEditionFeature,
@@ -121,7 +120,6 @@ const router = useRouter();
const message = useMessage();
const toast = useToast();
const folderHelpers = useFolders();
const calloutHelpers = useCalloutHelpers();
const sourceControlStore = useSourceControlStore();
const usersStore = useUsersStore();
@@ -461,19 +459,6 @@ const showAIStarterCollectionCallout = computed(() => {
);
});
const showPrebuiltAgentsCallout = computed(() => {
return (
!loading.value &&
calloutHelpers.isPreBuiltAgentsCalloutVisible.value &&
!calloutHelpers.isCalloutDismissed('preBuiltAgentsModalCallout') &&
!readOnlyEnv.value &&
// We want to show the callout only if the user has permissions to create folders and workflows
// but also on the overview page
(projectPages.isOverviewSubPage ||
(hasPermissionToCreateFolders.value && hasPermissionToCreateWorkflows.value))
);
});
const showPersonalizedTemplates = computed(
() => !loading.value && personalizedTemplatesStore.isFeatureEnabled(),
);
@@ -484,7 +469,6 @@ const shouldUseSimplifiedLayout = computed(() => {
const hasActiveCallouts = computed(() => {
return (
showPrebuiltAgentsCallout.value ||
showAIStarterCollectionCallout.value ||
showPersonalizedTemplates.value ||
showReadyToRunWorkflowsCallout.value
@@ -964,10 +948,6 @@ const createAIStarterWorkflows = async (source: 'card' | 'callout') => {
}
};
const openPrebuiltAgentsModal = (source: 'workflowsEmptyState' | 'workflowsList') => {
void calloutHelpers.openPreBuiltAgentsModal(source);
};
const handleCreateReadyToRunWorkflows = async (source: 'card' | 'callout') => {
try {
const projectId = projectPages.isOverviewSubPage
@@ -1017,10 +997,6 @@ const dismissEasyAICallout = () => {
easyAICalloutVisible.value = false;
};
const dismissPreBuiltAgentsCallout = () => {
void calloutHelpers.dismissCallout('preBuiltAgentsModalCallout');
};
const openAIWorkflow = async (source: string) => {
dismissEasyAICallout();
telemetry.track('User clicked test AI workflow', {
@@ -1867,39 +1843,7 @@ const onNameSubmit = async (name: string) => {
</template>
<template #callout>
<N8nCallout
v-if="showPrebuiltAgentsCallout"
theme="secondary"
icon="bot"
icon-size="large"
:class="$style['easy-ai-workflow-callout']"
>
<N8nText size="small">
{{ i18n.baseText('workflows.preBuiltAgents.callout') }}
{{ ' ' }}
<N8nLink
theme="secondary"
size="small"
:bold="true"
:underline="true"
@click="openPrebuiltAgentsModal('workflowsEmptyState')"
>
{{ i18n.baseText('workflows.preBuiltAgents.linkText') }}
</N8nLink>
</N8nText>
<template #trailingContent>
<div :class="$style['callout-trailing-content']">
<N8nIcon
size="small"
icon="x"
:title="i18n.baseText('generic.dismiss')"
class="clickable"
@click="dismissPreBuiltAgentsCallout()"
/>
</div>
</template>
</N8nCallout>
<N8nCallout
v-else-if="showAIStarterCollectionCallout"
v-if="showAIStarterCollectionCallout"
theme="secondary"
icon="gift"
:class="$style['easy-ai-workflow-callout']"
@@ -2134,26 +2078,7 @@ const onNameSubmit = async (name: string) => {
</div>
</N8nCard>
<N8nCard
v-if="showPrebuiltAgentsCallout"
:class="$style.emptyStateCard"
hoverable
data-test-id="prebuilt-agents-card"
@click="openPrebuiltAgentsModal('workflowsList')"
>
<div :class="$style.emptyStateCardContent">
<N8nIcon
:class="$style.emptyStateCardIcon"
:stroke-width="1.5"
icon="bot"
color="foreground-dark"
/>
<N8nText size="large" class="mt-xs pl-2xs pr-2xs">
{{ i18n.baseText('workflows.empty.preBuiltAgents') }}
</N8nText>
</div>
</N8nCard>
<N8nCard
v-else-if="showAIStarterCollectionCallout"
v-if="showAIStarterCollectionCallout"
:class="$style.emptyStateCard"
hoverable
data-test-id="easy-ai-workflow-card"
@@ -835,30 +835,6 @@ describe('ParameterInputList', () => {
expect(getByText('AI Agent Starter Callout')).toBeInTheDocument();
});
it('should handle preBuiltAgentsCallout visibility', () => {
const preBuiltCalloutParameters: INodeProperties[] = [
{
displayName: 'Pre-built Agents Callout',
name: 'preBuiltAgentsCallout_test',
type: 'callout',
default: '',
},
];
ndvStore.activeNode = TEST_NODE_NO_ISSUES;
const { container, getByTestId } = renderComponent({
props: {
parameters: preBuiltCalloutParameters,
nodeValues: TEST_NODE_VALUES,
},
});
// Callout with preBuiltAgentsCallout name pattern visibility is controlled by isPreBuiltAgentsCalloutVisible
// which depends on store state not mocked here, so it may or may not be visible
expect(container.querySelector('.parameter-input-list-wrapper')).toBeInTheDocument();
expect(getByTestId('parameter-item')).toBeInTheDocument();
});
});
/**
@@ -98,10 +98,8 @@ const i18n = useI18n();
const {
dismissCallout,
isCalloutDismissed,
openPreBuiltAgentsCollection,
openSampleWorkflowTemplate,
isRagStarterCalloutVisible,
isPreBuiltAgentsCalloutVisible,
} = useCalloutHelpers();
const { activeNode } = storeToRefs(ndvStore);
@@ -478,14 +476,6 @@ function isRagStarterCallout(parameter: INodeProperties): boolean {
return parameter.type === 'callout' && parameter.name === 'ragStarterCallout';
}
function isAgentDefaultCallout(parameter: INodeProperties): boolean {
return parameter.type === 'callout' && parameter.name === 'aiAgentStarterCallout';
}
function isPreBuiltAgentsCallout(parameter: INodeProperties): boolean {
return parameter.type === 'callout' && parameter.name.startsWith('preBuiltAgentsCallout');
}
function isCalloutVisible(parameter: INodeProperties): boolean {
if (isCalloutDismissed(parameter.name)) return false;
@@ -493,28 +483,11 @@ function isCalloutVisible(parameter: INodeProperties): boolean {
return isRagStarterCalloutVisible.value;
}
if (isAgentDefaultCallout(parameter)) {
return !isPreBuiltAgentsCalloutVisible.value;
}
if (isPreBuiltAgentsCallout(parameter)) {
return isPreBuiltAgentsCalloutVisible.value;
}
return true;
}
function onCalloutAction(action: CalloutAction) {
switch (action.type) {
case 'openPreBuiltAgentsCollection':
void openPreBuiltAgentsCollection({
telemetry: {
source: 'ndv',
nodeType: activeNode.value?.type,
},
resetStacks: false,
});
break;
case 'openSampleWorkflowTemplate':
void openSampleWorkflowTemplate(action.templateId, {
telemetry: {
@@ -10,6 +10,7 @@ import type { CommandGroup, CommandBarItem } from '../types';
import { CHAT_VIEW } from '@/features/ai/chatHub/constants';
import { useSettingsStore } from '@/app/stores/settings.store';
import { useProjectsStore } from '@/features/collaboration/projects/projects.store';
import { useTemplatesStore } from '@/features/workflows/templates/templates.store';
const ITEM_ID = {
CHAT_HUB: 'chat-hub',
@@ -33,6 +34,7 @@ export function useGenericCommands(): CommandGroup {
const router = useRouter();
const settingsStore = useSettingsStore();
const projectsStore = useProjectsStore();
const templatesStore = useTemplatesStore();
const { getReportingURL } = useBugReporting();
const genericCommands = computed<CommandBarItem[]>(() => [
@@ -80,7 +82,11 @@ export function useGenericCommands(): CommandGroup {
title: i18n.baseText('generic.templates'),
section: i18n.baseText('commandBar.sections.general'),
handler: () => {
void router.push({ name: VIEWS.PRE_BUILT_AGENT_TEMPLATES });
if (templatesStore.hasCustomTemplatesHost) {
void router.push({ name: VIEWS.TEMPLATES });
} else {
window.open(templatesStore.websiteTemplateRepositoryURL, '_blank');
}
},
icon: {
component: N8nIcon,
@@ -29,7 +29,7 @@ import { useTelemetry } from '@/app/composables/useTelemetry';
import { useI18n } from '@n8n/i18n';
import { useNodeCreatorStore } from '@/features/shared/nodeCreator/nodeCreator.store';
import OrderSwitcher from './../OrderSwitcher.vue';
import { getActiveViewCallouts, isNodePreviewKey } from '../../nodeCreator.utils';
import { isNodePreviewKey } from '../../nodeCreator.utils';
import CommunityNodeInfo from '@/features/settings/communityNodes/components/nodeCreator/CommunityNodeInfo.vue';
import CommunityNodeFooter from '@/features/settings/communityNodes/components/nodeCreator/CommunityNodeFooter.vue';
@@ -55,7 +55,7 @@ const {
} = useActions();
const nodeCreatorStore = useNodeCreatorStore();
const calloutHelpers = useCalloutHelpers();
const { openSampleWorkflowTemplate } = useCalloutHelpers();
// We only inject labels if search is empty
const parsedTriggerActions = computed(() =>
@@ -163,7 +163,7 @@ function onKeySelect(activeItemId: string) {
function onSelected(actionCreateElement: INodeCreateElement) {
if (actionCreateElement.type === 'openTemplate') {
calloutHelpers.openSampleWorkflowTemplate(actionCreateElement.properties.templateId, {
openSampleWorkflowTemplate(actionCreateElement.properties.templateId, {
telemetry: {
source: 'nodeCreator',
section: useViewStacks().activeViewStack.title,
@@ -247,13 +247,7 @@ onMounted(() => {
trackActionsView();
});
const callouts = computed<INodeCreateElement[]>(() =>
getActiveViewCallouts(
useViewStacks().activeViewStack.title,
calloutHelpers.isPreBuiltAgentsCalloutVisible.value,
calloutHelpers.getPreBuiltAgentNodeCreatorItems(),
),
);
const callouts = computed<INodeCreateElement[]>(() => []);
</script>
<template>
@@ -16,7 +16,6 @@ import {
AI_NODE_CREATOR_VIEW,
AI_OTHERS_NODE_CREATOR_VIEW,
HITL_SUBCATEGORY,
PRE_BUILT_AGENTS_COLLECTION,
} from '@/app/constants';
import type { BaseTextKey } from '@n8n/i18n';
@@ -29,7 +28,6 @@ import {
prepareCommunityNodeDetailsViewStack,
transformNodeType,
getRootSearchCallouts,
getActiveViewCallouts,
shouldShowCommunityNodeDetails,
getHumanInTheLoopActions,
} from '../../nodeCreator.utils';
@@ -58,7 +56,7 @@ const emit = defineEmits<{
const i18n = useI18n();
const calloutHelpers = useCalloutHelpers();
const { isRagStarterCalloutVisible, openSampleWorkflowTemplate } = useCalloutHelpers();
const { mergedNodes, actions, onSubcategorySelected } = useNodeCreatorStore();
const { pushViewStack, popViewStack, isAiSubcategoryView } = useViewStacks();
@@ -105,17 +103,6 @@ function getFilteredActions(
}
function onSelected(item: INodeCreateElement) {
if (item.key === PRE_BUILT_AGENTS_COLLECTION) {
void calloutHelpers.openPreBuiltAgentsCollection({
telemetry: {
source: 'nodeCreator',
section: activeViewStack.value.title,
},
resetStacks: false,
});
return;
}
if (item.type === 'subcategory') {
const subcategoryKey = camelCase(item.properties.title);
const title = i18n.baseText(`nodeCreator.subcategoryNames.${subcategoryKey}` as BaseTextKey);
@@ -235,7 +222,7 @@ function onSelected(item: INodeCreateElement) {
}
if (item.type === 'openTemplate') {
calloutHelpers.openSampleWorkflowTemplate(item.properties.templateId, {
openSampleWorkflowTemplate(item.properties.templateId, {
telemetry: {
source: 'nodeCreator',
section: activeViewStack.value.title,
@@ -282,13 +269,8 @@ function baseSubcategoriesFilter(item: INodeCreateElement): boolean {
const globalCallouts = computed<INodeCreateElement[]>(() => [
...getRootSearchCallouts(activeViewStack.value.search ?? '', {
isRagStarterCalloutVisible: calloutHelpers.isRagStarterCalloutVisible.value,
isRagStarterCalloutVisible: isRagStarterCalloutVisible.value,
}),
...getActiveViewCallouts(
activeViewStack.value.title,
calloutHelpers.isPreBuiltAgentsCalloutVisible.value,
calloutHelpers.getPreBuiltAgentNodeCreatorItems(),
),
]);
function arrowLeft() {
@@ -9,22 +9,17 @@ import type {
NodeFilterType,
OpenTemplateElement,
LinkCreateElement,
ViewCreateElement,
} from '@/Interface';
import {
AI_CATEGORY_AGENTS,
AI_CATEGORY_OTHER_TOOLS,
AI_CATEGORY_TOOLS,
AI_SUBCATEGORY,
AI_TRANSFORM_NODE_TYPE,
AI_CATEGORY_LANGUAGE_MODELS,
AI_CATEGORY_MEMORY,
CORE_NODES_CATEGORY,
DEFAULT_SUBCATEGORY,
DISCORD_NODE_TYPE,
HUMAN_IN_THE_LOOP_CATEGORY,
MICROSOFT_TEAMS_NODE_TYPE,
PRE_BUILT_AGENTS_COLLECTION,
RECOMMENDED_NODES,
BETA_NODES,
} from '@/app/constants';
@@ -42,10 +37,7 @@ import { useNodeTypesStore } from '@/app/stores/nodeTypes.store';
import { SEND_AND_WAIT_OPERATION } from 'n8n-workflow';
import type { NodeIconSource } from '@/app/utils/nodeIcon';
import type { CommunityNodeDetails, ViewStack } from './composables/useViewStacks';
import {
PrebuiltAgentTemplates,
SampleTemplates,
} from '@/features/workflows/templates/utils/workflowSamples';
import { SampleTemplates } from '@/features/workflows/templates/utils/workflowSamples';
const COMMUNITY_NODE_TYPE_PREVIEW_TOKEN = '-preview';
@@ -362,45 +354,6 @@ export function getRagStarterCallout(): OpenTemplateElement {
};
}
// Callout without a divider
export function getPreBuiltAgentsCallout(): ViewCreateElement {
return {
uuid: uuidv4(),
key: PRE_BUILT_AGENTS_COLLECTION,
type: 'view',
properties: {
title: i18n.baseText('nodeCreator.preBuiltAgents.title'),
icon: 'box',
description: i18n.baseText('nodeCreator.preBuiltAgents.description'),
borderless: true,
tag: {
type: 'info',
text: i18n.baseText('generic.recommended'),
},
},
};
}
// Callout with divider after it
export function getPreBuiltAgentsCalloutWithDivider(): LinkCreateElement {
return {
uuid: uuidv4(),
key: PRE_BUILT_AGENTS_COLLECTION,
type: 'link',
properties: {
key: PRE_BUILT_AGENTS_COLLECTION,
url: '',
title: i18n.baseText('nodeCreator.preBuiltAgents.title'),
icon: 'box',
description: i18n.baseText('nodeCreator.preBuiltAgents.description'),
tag: {
type: 'info',
text: i18n.baseText('generic.recommended'),
},
},
};
}
export function getAiTemplatesCallout(aiTemplatesURL: string): LinkCreateElement {
return {
uuid: 'ai_templates_root',
@@ -430,62 +383,6 @@ export function getRootSearchCallouts(search: string, { isRagStarterCalloutVisib
return results;
}
const getTemplateLink = (
templateId: string,
availableTemplates: OpenTemplateElement[],
): OpenTemplateElement | undefined => {
const templateLink = availableTemplates.find((template) => template.key === templateId);
if (templateLink?.properties) {
templateLink.properties.compact = true;
}
return templateLink;
};
export function getActiveViewCallouts(
title: string | undefined,
isPreBuiltAgentsCalloutVisible: boolean,
templates: OpenTemplateElement[],
) {
const results: INodeCreateElement[] = [];
if (isPreBuiltAgentsCalloutVisible && title) {
if (title === AI_CATEGORY_LANGUAGE_MODELS) {
results.push(getPreBuiltAgentsCalloutWithDivider());
} else if ([AI_CATEGORY_MEMORY, AI_CATEGORY_TOOLS].includes(title)) {
results.push(getPreBuiltAgentsCallout());
} else if (title === 'Google Calendar') {
const templateLink = getTemplateLink(PrebuiltAgentTemplates.CalendarAgent, templates);
if (templateLink) {
results.push(templateLink);
}
} else if (title === 'Telegram') {
const templateLink = getTemplateLink(PrebuiltAgentTemplates.VoiceAssistantAgent, templates);
if (templateLink) {
results.push(templateLink);
}
} else if (title === 'Google Drive') {
const templateLink = getTemplateLink(PrebuiltAgentTemplates.KnowledgeStoreAgent, templates);
if (templateLink) {
results.push(templateLink);
}
} else if (title === 'Google Sheets') {
const templateLink = getTemplateLink(PrebuiltAgentTemplates.TaskManagementAgent, templates);
if (templateLink) {
results.push(templateLink);
}
} else if (title === 'Gmail') {
const templateLink = getTemplateLink(PrebuiltAgentTemplates.EmailTriageAgent, templates);
if (templateLink) {
results.push(templateLink);
}
}
}
return results;
}
export const shouldShowCommunityNodeDetails = (communityNode: boolean, viewStack: ViewStack) => {
if (viewStack.rootView === 'AI Other' && viewStack.title === 'Tools') {
return false;
@@ -1,20 +1,12 @@
import { setActivePinia } from 'pinia';
import { createTestingPinia } from '@pinia/testing';
import {
AI_CATEGORY_AGENTS,
AI_CATEGORY_CHAINS,
AI_TRANSFORM_NODE_TYPE,
PRE_BUILT_AGENTS_EXPERIMENT,
} from '@/app/constants';
import { AI_CATEGORY_AGENTS, AI_CATEGORY_CHAINS, AI_TRANSFORM_NODE_TYPE } from '@/app/constants';
import type { INodeTypeDescription } from 'n8n-workflow';
import { MANUAL_TRIGGER_NODE_TYPE } from 'n8n-workflow';
import { useSettingsStore } from '@/app/stores/settings.store';
import { AIView } from './viewsData';
import { mockNodeTypeDescription } from '@/__tests__/mocks';
import { useTemplatesStore } from '@/features/workflows/templates/templates.store';
import { usePostHog } from '@/app/stores/posthog.store';
let posthogStore: ReturnType<typeof usePostHog>;
const getNodeType = vi.fn();
@@ -59,16 +51,6 @@ describe('viewsData', () => {
beforeAll(() => {
setActivePinia(createTestingPinia());
posthogStore = usePostHog();
vi.spyOn(posthogStore, 'isVariantEnabled').mockImplementation((experiment) => {
if (experiment === PRE_BUILT_AGENTS_EXPERIMENT.name) {
return false;
}
return true;
});
const templatesStore = useTemplatesStore();
vi.spyOn(templatesStore, 'websiteTemplateRepositoryParameters', 'get').mockImplementation(
@@ -106,8 +88,6 @@ describe('viewsData', () => {
});
test('should return ai view without ai transform node if ask ai is not enabled and node is not in the list', () => {
vi.spyOn(posthogStore, 'isVariantEnabled').mockReturnValue(false);
const settingsStore = useSettingsStore();
vi.spyOn(settingsStore, 'isAskAiEnabled', 'get').mockReturnValue(false);
@@ -70,8 +70,7 @@ import type { BaseTextKey } from '@n8n/i18n';
import camelCase from 'lodash/camelCase';
import { useSettingsStore } from '@/app/stores/settings.store';
import { useEvaluationStore } from '@/features/ai/evaluation.ee/evaluation.store';
import { getAiTemplatesCallout, getPreBuiltAgentsCalloutWithDivider } from '../nodeCreator.utils';
import { useCalloutHelpers } from '@/app/composables/useCalloutHelpers';
import { getAiTemplatesCallout } from '../nodeCreator.utils';
export interface NodeViewItemSection {
key: string;
@@ -173,7 +172,6 @@ export function AIView(_nodes: SimplifiedNodeType[]): NodeView {
const nodeTypesStore = useNodeTypesStore();
const templatesStore = useTemplatesStore();
const evaluationStore = useEvaluationStore();
const calloutHelpers = useCalloutHelpers();
const isEvaluationEnabled = evaluationStore.isEvaluationEnabled;
const evaluationNode = getEvaluationNode(nodeTypesStore, isEvaluationEnabled);
@@ -192,9 +190,7 @@ export function AIView(_nodes: SimplifiedNodeType[]): NodeView {
const aiTransformNode = nodeTypesStore.getNodeType(AI_TRANSFORM_NODE_TYPE);
const transformNode = askAiEnabled && aiTransformNode ? [getNodeView(aiTransformNode)] : [];
const callouts: NodeViewItem[] = !calloutHelpers.isPreBuiltAgentsCalloutVisible.value
? [getAiTemplatesCallout(aiTemplatesURL)]
: [getPreBuiltAgentsCalloutWithDivider()];
const callouts: NodeViewItem[] = [getAiTemplatesCallout(aiTemplatesURL)];
return {
value: AI_NODE_CREATOR_VIEW,
@@ -1,170 +0,0 @@
{
"name": "Calendar agent",
"meta": {
"templateCredsSetupCompleted": false,
"templateId": "calendar_agent_with_gcal"
},
"nodes": [
{
"parameters": {
"resource": "calendar",
"calendar": {},
"timeMin": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Start_Time', `Start date for availability check`, 'string') }}",
"timeMax": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('End_Time', `End date for availability check`, 'string') }}",
"options": {}
},
"id": "e3eb2815-735f-4efd-a844-da9d53e45aae",
"name": "Get availability",
"type": "n8n-nodes-base.googleCalendarTool",
"typeVersion": 1.3,
"position": [-32, 224],
"credentials": {}
},
{
"parameters": {
"operation": "getAll",
"calendar": {},
"returnAll": true,
"timeMin": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('After', `Start date for events search`, 'string') }}",
"timeMax": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Before', `End date for events search`, 'string') }}",
"options": {}
},
"id": "d6c00690-8d0c-4942-aa17-077e61c909d1",
"name": "Get events",
"type": "n8n-nodes-base.googleCalendarTool",
"typeVersion": 1.3,
"position": [112, 224],
"credentials": {}
},
{
"parameters": {
"options": {}
},
"id": "719aaeb8-e985-4235-bcff-c2bfbc6a8e5c",
"name": "Chat message",
"type": "@n8n/n8n-nodes-langchain.chatTrigger",
"typeVersion": 1.3,
"position": [-400, -32],
"webhookId": "d0b3a15a-8d1c-437c-8523-00e108af14a4"
},
{
"parameters": {
"model": {
"__rl": true,
"mode": "id",
"value": "gpt-4o-mini"
},
"options": {
"temperature": 0
}
},
"id": "d23e09d0-ff7c-4f1b-8e67-6864801dbc44",
"name": "Model",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"typeVersion": 1.2,
"position": [-304, 224],
"credentials": {}
},
{
"parameters": {},
"type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
"typeVersion": 1.3,
"position": [-176, 224],
"id": "56bd33c7-bb31-4957-9f12-fdcc2351880d",
"name": "Simple Memory"
},
{
"parameters": {
"options": {
"systemMessage": "=You are a helpful calendar assistant. You can check availability and retrieve events from Google Calendar. When users ask about dates, use the calendar tools to get the information. Always specify date ranges clearly when using the tools.\n\nTodays date is: {{ $now }}",
"maxIterations": 10,
"returnIntermediateSteps": false,
"passthroughBinaryImages": true,
"batching": {},
"enableStreaming": true
}
},
"id": "1e8be9e3-24a4-415b-9424-6f9010c5f333",
"name": "Calendar agent",
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 2.2,
"position": [-160, -32]
},
{
"parameters": {
"content": "## Start here!\n@[youtube](-S5k4sXrPws)",
"height": 320,
"width": 464,
"color": 5
},
"type": "n8n-nodes-base.stickyNote",
"position": [-928, -32],
"typeVersion": 1,
"id": "d3552acd-20f1-40e0-af3c-e2aabdbf3f27",
"name": "Sticky Note"
}
],
"connections": {
"Get availability": {
"ai_tool": [
[
{
"node": "Calendar agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"Get events": {
"ai_tool": [
[
{
"node": "Calendar agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"Chat message": {
"main": [
[
{
"node": "Calendar agent",
"type": "main",
"index": 0
}
]
]
},
"Model": {
"ai_languageModel": [
[
{
"node": "Calendar agent",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Simple Memory": {
"ai_memory": [
[
{
"node": "Calendar agent",
"type": "ai_memory",
"index": 0
}
]
]
}
},
"pinData": {},
"active": false,
"settings": {
"executionOrder": "v1"
},
"tags": []
}
@@ -1,156 +0,0 @@
{
"name": "Email Triage Agent (with Gmail)",
"meta": {
"templateCredsSetupCompleted": false,
"templateId": "email_triage_agent_with_gmail"
},
"nodes": [
{
"parameters": {
"pollTimes": {
"item": [
{
"mode": "everyMinute"
}
]
},
"simple": false,
"filters": {
"readStatus": "unread"
},
"options": {}
},
"id": "0280aa41-d55c-47e4-b99b-03b4c7c9e4e7",
"name": "New Email Trigger",
"type": "n8n-nodes-base.gmailTrigger",
"typeVersion": 1.3,
"position": [-64, -32],
"credentials": {}
},
{
"parameters": {
"promptType": "define",
"text": "=Categorize this email:\n\n**From:** {{ $json.from.value[0].address }}\n**Subject:** {{ $json.subject }}\n**Content:** \n{{ $json.html }}\n\nPlease analyze this email and apply appropriate labels using the available tools.",
"options": {
"systemMessage": "=You are an email categorization assistant. Your task is to analyze emails and apply appropriate labels to help organize the user's inbox.\n\nYou have access to two tools:\n1. **Get all Labels** - Retrieves available email labels\n2. **Add Labels** - Applies labels to emails\n\n### Instructions:\n- First, always use \"Get all Labels\" to see available categories\n- Analyze email content for: subject, sender, keywords, and context\n- Apply 1-3 most relevant labels per email\n- Be consistent in categorization patterns\n- If unsure between labels, choose the most specific one\n\n### Response Format:\n1. Brief summary of email content (1 sentence)\n2. Chosen labels with reasoning\n3. Use \"Add Labels\" tool with selected labels\n\nBe efficient and accurate. Focus on helping the user maintain an organized inbox."
}
},
"id": "a4dccc7a-008e-40c5-90dd-c91090a144fd",
"name": "Email Classification Agent",
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 2.2,
"position": [224, -32]
},
{
"parameters": {
"operation": "addLabels",
"messageId": "={{ $json.id }}",
"labelIds": "={{ $fromAI('Label_Names_or_IDs', `Array of Gmail label ids to add. Use the id field from the \"Get all labels\" tool`, 'string') }}"
},
"id": "a5e4168b-1ce7-4cfe-ae6d-a1a4ab9b96df",
"name": "Add Label",
"type": "n8n-nodes-base.gmailTool",
"typeVersion": 2.1,
"position": [320, 192],
"webhookId": "e7289911-d937-45bd-9e91-410a0dc959f8",
"credentials": {}
},
{
"parameters": {
"descriptionType": "manual",
"toolDescription": "Use this tool to get all existing labels from Gmail",
"resource": "label",
"returnAll": true
},
"type": "n8n-nodes-base.gmailTool",
"typeVersion": 2.1,
"position": [464, 192],
"id": "79ee166d-4443-45c6-84e1-8820299cf2de",
"name": "Get all Labels",
"webhookId": "291b9995-7ae1-4f65-9ffb-ba62c980b829",
"credentials": {}
},
{
"parameters": {
"content": "## Start here!\n@[youtube](U1zxP7Ut1Kc)",
"height": 320,
"width": 464,
"color": 5
},
"type": "n8n-nodes-base.stickyNote",
"position": [-608, -144],
"typeVersion": 1,
"id": "db4fcd5d-7f0a-41f6-9dd7-4100a63f38cc",
"name": "Sticky Note"
},
{
"parameters": {
"model": {
"__rl": true,
"value": "gpt-4.1-mini",
"mode": "list",
"cachedResultName": "gpt-4.1-mini"
},
"options": {}
},
"id": "1fda1d78-a669-4be1-9aba-56c0abb7c247",
"name": "Model",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"typeVersion": 1.2,
"position": [160, 192],
"credentials": {}
}
],
"pinData": {},
"connections": {
"New Email Trigger": {
"main": [
[
{
"node": "Email Classification Agent",
"type": "main",
"index": 0
}
]
]
},
"Add Label": {
"ai_tool": [
[
{
"node": "Email Classification Agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"Get all Labels": {
"ai_tool": [
[
{
"node": "Email Classification Agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"Model": {
"ai_languageModel": [
[
{
"node": "Email Classification Agent",
"type": "ai_languageModel",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"tags": []
}
@@ -1,168 +0,0 @@
{
"name": "Joke agent (with HTTP tool)",
"meta": {
"templateCredsSetupCompleted": false,
"templateId": "joke_agent_with_http_tool"
},
"nodes": [
{
"parameters": {
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.chatTrigger",
"typeVersion": 1.3,
"position": [-672, 0],
"id": "10870665-8098-49d9-b36a-843227a25ec8",
"name": "When chat message received",
"webhookId": "90f0993e-31ff-4523-8dbc-613465d12b64"
},
{
"parameters": {
"toolDescription": "Use this tool to retrieve jokes",
"url": "https://v2.jokeapi.dev/joke/",
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('parameters0_Name', ``, 'string') }}",
"value": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('parameters0_Value', ``, 'string') }}"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.httpRequestTool",
"typeVersion": 4.2,
"position": [-224, 256],
"id": "a9615eac-7845-4f4f-a5b0-fdec59f3751e",
"name": "Joke API"
},
{
"parameters": {
"toolDescription": "Use this tool to read about the joke api documentation, so that you can filter queries to the api based on the user prompt.",
"url": "https://v2.jokeapi.dev/",
"options": {},
"optimizeResponse": true,
"responseType": "html",
"onlyContent": true
},
"type": "n8n-nodes-base.httpRequestTool",
"typeVersion": 4.2,
"position": [-48, 256],
"id": "a8f4b23a-89cb-4673-a595-6855ba1a6d10",
"name": "API docs"
},
{
"parameters": {
"model": {
"__rl": true,
"mode": "list",
"value": "gpt-4.1-mini"
},
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"typeVersion": 1.2,
"position": [-576, 272],
"id": "93fdc1ff-4dee-4f7f-8b37-b8c041a22073",
"name": "Model",
"credentials": {}
},
{
"parameters": {},
"type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
"typeVersion": 1.3,
"position": [-400, 272],
"id": "8ca3ae47-7e3d-42ed-ac35-bf2f187715eb",
"name": "Memory"
},
{
"parameters": {
"options": {
"systemMessage": "# Joke Agent System Prompt\n\nYou are a fun and entertaining joke bot that brings laughter and good vibes to conversations. Your job is to tell jokes, share humor, and keep things light and cheerful.\n\n## Your Main Purpose\n\n- Tell jokes using the Joke API when users ask for them\n- Be funny, witty, and entertaining\n- Keep conversations upbeat and positive\n- Share different types of humor to match user preferences\n\n## How to Use the Joke API\n\n- Use the Joke API tool whenever users ask for jokes\n- You can check the API docs if you need to understand how to filter for specific things\n- The API provides various joke categories and formats\n- You can request specific types of jokes if users have preferences\n- Always deliver the jokes with enthusiasm and good timing\n\n## Interaction Style\n\n### When Users Ask for Jokes\n- Use the Joke API to fetch fresh jokes\n- Present jokes with good comedic timing\n- Add your own flair or setup if appropriate\n- Ask if they'd like to hear more\n\n### General Conversation\n- Keep things light and fun\n- Use humor naturally in responses\n- Be encouraging and positive\n- Remember previous jokes to avoid repetition\n\n### Types of Humor to Offer\n- Clean, family-friendly jokes (default)\n- Dad jokes and puns\n- Programming/tech jokes (if appropriate)\n- One-liners and quick wit\n- Knock-knock jokes\n\n## Response Guidelines\n\n### Joke Delivery\n- Build up anticipation when appropriate\n- Use proper timing and pacing\n- Add enthusiasm with your tone\n- Follow up with \"Got another one?\" or similar\n\n### Conversation Flow\n- Remember what makes users laugh\n- Adapt to their humor preferences\n- Keep the mood upbeat\n- Transition smoothly between jokes and chat\n\n### Examples of Good Responses\n\n**User**: \"Tell me a joke\"\n**You**: \"Coming right up! Let me grab a good one for you...\" *[uses Joke API]* \n\n**User**: \"That was terrible!\"\n**You**: \"Hey, they can't all be winners! 😄 Want me to try again with a different style?\"\n\n**User**: \"Got any programming jokes?\"\n**You**: \"Oh, I love tech humor! Let me find you a good one...\" *[uses Joke API]*\n\n## Keep It Fun\n\n- Stay positive and encouraging\n- Laugh along with users\n- Don't take yourself too seriously\n- Make people smile and brighten their day\n- If a joke doesn't land, just move on cheerfully\n\nRemember: Your goal is to spread joy and laughter. Keep things fun, appropriate, and entertaining!"
}
},
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 2.2,
"position": [-448, 0],
"id": "fb541fe9-749d-4f22-a171-3a16eb1bdb4c",
"name": "Joke agent"
},
{
"parameters": {
"content": "## Start here!\n@[youtube](q1TMyj3zrp0)",
"height": 320,
"width": 464,
"color": 5
},
"type": "n8n-nodes-base.stickyNote",
"position": [-1200, 0],
"typeVersion": 1,
"id": "b30452e2-c811-4191-8415-7ae444b15849",
"name": "Sticky Note"
}
],
"pinData": {},
"connections": {
"When chat message received": {
"main": [
[
{
"node": "Joke agent",
"type": "main",
"index": 0
}
]
]
},
"Joke API": {
"ai_tool": [
[
{
"node": "Joke agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"API docs": {
"ai_tool": [
[
{
"node": "Joke agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"Model": {
"ai_languageModel": [
[
{
"node": "Joke agent",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Memory": {
"ai_memory": [
[
{
"node": "Joke agent",
"type": "ai_memory",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"tags": []
}
@@ -1,284 +0,0 @@
{
"name": "Knowledge store agent (with Google Drive)",
"meta": {
"templateCredsSetupCompleted": false,
"templateId": "knowledge_store_agent_with_google_drive"
},
"nodes": [
{
"parameters": {
"dataType": "binary",
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.documentDefaultDataLoader",
"typeVersion": 1.1,
"position": [-128, 256],
"id": "708d9e4e-6566-4b18-86b8-8301bf0c72dd",
"name": "Default Data Loader"
},
{
"parameters": {
"operation": "download",
"fileId": {
"__rl": true,
"value": "={{ $json.id }}",
"mode": "id"
},
"options": {}
},
"type": "n8n-nodes-base.googleDrive",
"typeVersion": 3,
"position": [-432, 32],
"id": "604a4e5e-5ccc-4db1-94dc-673711bea6af",
"name": "Download file",
"credentials": {}
},
{
"parameters": {
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.embeddingsOpenAi",
"typeVersion": 1.2,
"position": [-336, 656],
"id": "e8db240e-bb3f-43ab-b4ed-527c6c2e8f54",
"name": "Embedding model",
"credentials": {}
},
{
"parameters": {
"options": {
"systemMessage": "# Knowledge Store Agent System Prompt\n\nYou are a data analysis agent that retrieves and analyzes information from a vector store to answer user questions.\n\n## Your Task\n\n1. **Search the vector store** - Use similarity search to find relevant documents and data\n2. **Analyze the results** - Understand what the retrieved data tells you\n3. **Provide clear answers** - Give helpful responses based on the data you found\n\n## How to Work with Vector Data\n\n### Search Process\n- Use the user's question to search for similar content\n- Retrieve multiple relevant chunks of data\n- Look for patterns and connections across the results\n- Consider both exact matches and conceptually similar information\n\n### Analysis Guidelines\n- Read through all retrieved documents carefully\n- Identify key information that answers the user's question\n- Note any conflicting or incomplete information\n- Look for trends, patterns, or insights in the data\n\n### Response Format\n- Start with a direct answer to the user's question\n- Support your answer with specific information from the data\n- Cite which documents or sources your information comes from\n- Be clear about what you found and what you didn't find\n\n## Response Guidelines\n\n### When You Find Good Data\n- Give a confident, detailed answer\n- Include relevant quotes or data points\n- Explain how the information relates to their question\n- Offer additional insights if available\n\n### When Data is Limited\n- Be honest about what information is available\n- Share what you did find, even if partial\n- Suggest related questions you could help with\n- Don't make up information not in the data\n\n### When No Relevant Data is Found\n- Clearly state that you couldn't find relevant information\n- Suggest alternative ways to phrase the question\n- Offer to search for related topics\n\n## Key Principles\n\n- Always base answers on the retrieved data\n- Be transparent about your sources\n- Admit when information is unclear or missing\n- Help users understand what the data shows\n- Ask clarifying questions if the user's request is vague\n\nRemember: Your strength is finding and explaining information that already exists in the vector store. Focus on being accurate and helpful with the data you can retrieve."
}
},
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 2.2,
"position": [384, 32],
"id": "99f9315f-4f3d-48d9-a6f4-8c519b249eba",
"name": "AI Agent"
},
{
"parameters": {
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.chatTrigger",
"typeVersion": 1.3,
"position": [176, 32],
"id": "bedf3615-c8b0-4fa8-afc5-574bca1003d4",
"name": "When chat message received",
"webhookId": "ddf4ea5b-94fc-4fbf-b856-95d39a04eb59"
},
{
"parameters": {
"model": {
"__rl": true,
"value": "gpt-4o",
"mode": "list",
"cachedResultName": "gpt-4o"
},
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"typeVersion": 1.2,
"position": [288, 240],
"id": "9751b75b-50ac-49c8-8f4d-24a8035b6e73",
"name": "Model",
"credentials": {}
},
{
"parameters": {},
"type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
"typeVersion": 1.3,
"position": [432, 240],
"id": "167d097a-69f6-4f1b-a741-8c8936adb3d2",
"name": "Simple Memory"
},
{
"parameters": {
"pollTimes": {
"item": [
{
"mode": "everyMinute"
}
]
},
"triggerOn": "specificFolder",
"folderToWatch": {
"__rl": true,
"value": "1y55jxSWMeFBBFOIXxHWofVA2vkro4YLI",
"mode": "list",
"cachedResultName": "Knowledge store",
"cachedResultUrl": "https://drive.google.com/drive/folders/1y55jxSWMeFBBFOIXxHWofVA2vkro4YLI"
},
"event": "fileCreated",
"options": {}
},
"type": "n8n-nodes-base.googleDriveTrigger",
"typeVersion": 1,
"position": [-640, 32],
"id": "0cddc21e-a75f-48df-b168-8e288be12469",
"name": "File uploaded",
"credentials": {}
},
{
"parameters": {
"content": "### Embeddings\n\nThe Insert and Retrieve operation use the same embedding node.\n\nThis is to ensure that they are using the **exact same embeddings and settings**.\n\nDifferent embeddings might not work at all, or have unintended consequences.\n",
"height": 240,
"width": 320,
"color": 4
},
"type": "n8n-nodes-base.stickyNote",
"position": [-208, 656],
"typeVersion": 1,
"id": "2fa9684d-f20a-4262-9b8b-81babb2fecc5",
"name": "Sticky Note3"
},
{
"parameters": {
"mode": "insert",
"memoryKey": {
"__rl": true,
"value": "vector_store_key",
"mode": "list",
"cachedResultName": "vector_store_key"
}
},
"type": "@n8n/n8n-nodes-langchain.vectorStoreInMemory",
"typeVersion": 1.3,
"position": [-240, 32],
"id": "64d17551-1637-4a53-a3e0-db45894c12a6",
"name": "Insert documents"
},
{
"parameters": {
"mode": "retrieve-as-tool",
"toolDescription": "Use this tool to retrieve any information required.",
"memoryKey": {
"__rl": true,
"value": "vector_store_key",
"mode": "list",
"cachedResultName": "vector_store_key"
},
"topK": 10
},
"type": "@n8n/n8n-nodes-langchain.vectorStoreInMemory",
"typeVersion": 1.3,
"position": [640, 368],
"id": "4f8a4a45-28a4-4684-b838-66011cc6a4a5",
"name": "Retrieve documents"
},
{
"parameters": {
"content": "## Start here!\n@[youtube](v28IbZyoQEo)",
"height": 320,
"width": 464,
"color": 5
},
"type": "n8n-nodes-base.stickyNote",
"position": [-1152, 16],
"typeVersion": 1,
"id": "d5898954-ea28-4093-86d4-085887e8a34e",
"name": "Sticky Note1"
}
],
"pinData": {},
"connections": {
"Default Data Loader": {
"ai_document": [
[
{
"node": "Insert documents",
"type": "ai_document",
"index": 0
}
]
]
},
"Download file": {
"main": [
[
{
"node": "Insert documents",
"type": "main",
"index": 0
}
]
]
},
"Embedding model": {
"ai_embedding": [
[
{
"node": "Insert documents",
"type": "ai_embedding",
"index": 0
},
{
"node": "Retrieve documents",
"type": "ai_embedding",
"index": 0
}
]
]
},
"When chat message received": {
"main": [
[
{
"node": "AI Agent",
"type": "main",
"index": 0
}
]
]
},
"Model": {
"ai_languageModel": [
[
{
"node": "AI Agent",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Simple Memory": {
"ai_memory": [
[
{
"node": "AI Agent",
"type": "ai_memory",
"index": 0
}
]
]
},
"File uploaded": {
"main": [
[
{
"node": "Download file",
"type": "main",
"index": 0
}
]
]
},
"Retrieve documents": {
"ai_tool": [
[
{
"node": "AI Agent",
"type": "ai_tool",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"tags": []
}
@@ -1,388 +0,0 @@
{
"name": "Task management agent (with Google Sheets)",
"meta": {
"templateCredsSetupCompleted": false,
"templateId": "task_management_agent_with_google_sheets"
},
"nodes": [
{
"parameters": {
"model": {
"__rl": true,
"value": "gpt-4o",
"mode": "list",
"cachedResultName": "gpt-4o"
},
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"typeVersion": 1.2,
"position": [-608, 272],
"id": "7ebac830-d775-47af-a56c-9ff428f385d0",
"name": "Model",
"credentials": {}
},
{
"parameters": {
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.chatTrigger",
"typeVersion": 1.3,
"position": [-656, -32],
"id": "3eb00ee5-cab6-4a07-b3d5-cee733bae08d",
"name": "When chat message received",
"webhookId": "df8223bf-119c-44d5-9bbf-f74ae26828e8"
},
{
"parameters": {
"contextWindowLength": 10
},
"type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
"typeVersion": 1.3,
"position": [-496, 272],
"id": "5341bef1-5ba0-4898-85c2-ca8741210079",
"name": "Simple Memory"
},
{
"parameters": {
"documentId": {
"__rl": true,
"value": "1mrfeRU7qMBc9fXF7oteX4i2s50XU2qKJZNQWgmxGG5o",
"mode": "list",
"cachedResultName": "Enrichment example",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1mrfeRU7qMBc9fXF7oteX4i2s50XU2qKJZNQWgmxGG5o/edit?usp=drivesdk"
},
"sheetName": {
"__rl": true,
"value": "gid=0",
"mode": "list",
"cachedResultName": "Tasks",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1mrfeRU7qMBc9fXF7oteX4i2s50XU2qKJZNQWgmxGG5o/edit#gid=0"
},
"options": {}
},
"type": "n8n-nodes-base.googleSheetsTool",
"typeVersion": 4.7,
"position": [-352, 272],
"id": "a1792c3e-4248-49e2-b4c2-db58c4eb1b59",
"name": "Get tasks",
"credentials": {}
},
{
"parameters": {
"operation": "append",
"documentId": {
"__rl": true,
"value": "1mrfeRU7qMBc9fXF7oteX4i2s50XU2qKJZNQWgmxGG5o",
"mode": "list",
"cachedResultName": "Enrichment example",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1mrfeRU7qMBc9fXF7oteX4i2s50XU2qKJZNQWgmxGG5o/edit?usp=drivesdk"
},
"sheetName": {
"__rl": true,
"value": "gid=0",
"mode": "list",
"cachedResultName": "Tasks",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1mrfeRU7qMBc9fXF7oteX4i2s50XU2qKJZNQWgmxGG5o/edit#gid=0"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"Task": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Task', ``, 'string') }}",
"Description": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Description', ``, 'string') }}",
"Deadline": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Deadline', ``, 'string') }}",
"Status": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Status', ``, 'string') }}"
},
"matchingColumns": [],
"schema": [
{
"id": "Task",
"displayName": "Task",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "Status",
"displayName": "Status",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "Description",
"displayName": "Description",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "Deadline",
"displayName": "Deadline",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
}
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {}
},
"type": "n8n-nodes-base.googleSheetsTool",
"typeVersion": 4.7,
"position": [-224, 272],
"id": "1ebd8dd5-13e0-4f57-bb37-a3a5d8cf4e80",
"name": "Create task",
"credentials": {}
},
{
"parameters": {
"operation": "update",
"documentId": {
"__rl": true,
"value": "1mrfeRU7qMBc9fXF7oteX4i2s50XU2qKJZNQWgmxGG5o",
"mode": "list",
"cachedResultName": "Enrichment example",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1mrfeRU7qMBc9fXF7oteX4i2s50XU2qKJZNQWgmxGG5o/edit?usp=drivesdk"
},
"sheetName": {
"__rl": true,
"value": "gid=0",
"mode": "list",
"cachedResultName": "Tasks",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1mrfeRU7qMBc9fXF7oteX4i2s50XU2qKJZNQWgmxGG5o/edit#gid=0"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"Task": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Task', ``, 'string') }}",
"Description": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Description', ``, 'string') }}",
"Deadline": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Deadline', ``, 'string') }}",
"Status": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Status', ``, 'string') }}",
"row_number": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('row_number__using_to_match_', ``, 'number') }}"
},
"matchingColumns": ["row_number"],
"schema": [
{
"id": "ID",
"displayName": "ID",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": true
},
{
"id": "Task",
"displayName": "Task",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "Status",
"displayName": "Status",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "Description",
"displayName": "Description",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "Deadline",
"displayName": "Deadline",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "row_number",
"displayName": "row_number",
"required": false,
"defaultMatch": false,
"display": true,
"type": "number",
"canBeUsedToMatch": true,
"readOnly": true,
"removed": false
}
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {}
},
"type": "n8n-nodes-base.googleSheetsTool",
"typeVersion": 4.7,
"position": [-96, 272],
"id": "a118d819-41ef-4323-a4d1-d83d70e41ec5",
"name": "Update task",
"credentials": {}
},
{
"parameters": {
"operation": "delete",
"documentId": {
"__rl": true,
"value": "1mrfeRU7qMBc9fXF7oteX4i2s50XU2qKJZNQWgmxGG5o",
"mode": "list",
"cachedResultName": "Enrichment example",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1mrfeRU7qMBc9fXF7oteX4i2s50XU2qKJZNQWgmxGG5o/edit?usp=drivesdk"
},
"sheetName": {
"__rl": true,
"value": "gid=0",
"mode": "list",
"cachedResultName": "Tasks",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1mrfeRU7qMBc9fXF7oteX4i2s50XU2qKJZNQWgmxGG5o/edit#gid=0"
},
"startIndex": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Start_Row_Number', ``, 'number') }}",
"numberToDelete": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Number_of_Rows_to_Delete', ``, 'number') }}"
},
"type": "n8n-nodes-base.googleSheetsTool",
"typeVersion": 4.7,
"position": [32, 272],
"id": "c15d7d0f-d24c-4e5a-aa3f-a23abf2f6853",
"name": "Delete task",
"credentials": {}
},
{
"parameters": {
"options": {
"systemMessage": "=# Task Management Agent System Prompt\n\nYou are a helpful task management assistant that helps users organize their tasks using a Google Sheets database. You can create, view, update, and delete tasks while maintaining data consistency.\n\n## Your Capabilities\n\nYou can help users with:\n- **View tasks** - Show current tasks and their status\n- **Create new tasks** - Add tasks with proper formatting\n- **Update tasks** - Modify existing task details or status\n- **Delete tasks** - Remove tasks after confirmation\n- **Track progress** - Monitor task status changes\n\n## Task Data Structure\n\nEach task has these fields:\n- **Task**: The task name/title (required - always ask if missing)\n- **Status**: Must be one of: \"TODO\", \"IN PROGRESS\", or \"DONE\" (default: \"TODO\")\n- **Description**: Optional additional details about the task\n- **Deadline**: Optional due date (accept various date formats)\n\nNote: Google Sheets automatically provides a row_number field for tracking - you don't need to manage IDs manually.\n\n## Important Rules\n\n### Status Management\n- Status must be exactly one of: \"TODO\", \"IN PROGRESS\", or \"DONE\"\n- Default new tasks to \"TODO\" status\n- When users say things like \"mark as complete\" or \"finish task\", change status to \"DONE\"\n- When users say \"start working on\" or \"begin task\", change status to \"IN PROGRESS\"\n\n### Task Creation Process\n1. Always ask for the task name if not provided\n2. Ask if they want to add a description (optional)\n3. Ask about deadline if relevant (optional)\n4. Set status to \"TODO\" by default\n5. Use the \"Create task\" tool to add to the sheet\n\n### Task Updates\n- Ask which task to update (by name)\n- Confirm what changes to make\n- Use \"Update task\" tool with the task name as the matching column\n- Provide clear feedback about what was changed\n\n### Task Deletion\n- **ALWAYS ask for confirmation before deleting any task**\n- Show the task details and ask \"Are you sure you want to delete this task?\"\n- Only proceed with deletion after explicit user confirmation\n- Use the row number from Google Sheets for the \"Start_Row_Number\" parameter\n- Set \"Number_of_Rows_to_Delete\" to 1 (unless specifically told otherwise)\n\n## User Interaction Guidelines\n\n### When Creating Tasks\n**User**: \"Add a new task\"\n**You**: \"I'd be happy to help you create a new task. What would you like to call this task?\"\n\n### When Information is Missing\n**User**: \"Create a task for tomorrow\"\n**You**: \"I can create a task with a deadline for tomorrow. What should I call this task?\"\n\n### When Deleting Tasks\n**User**: \"Delete the marketing task\"\n**You**: \"I found the task 'Marketing Campaign Planning'. Are you sure you want to delete this task? This action cannot be undone.\"\n\n### When Updating Status\n**User**: \"Mark the report as done\"\n**You**: \"I'll update the status of 'Monthly Report' to 'DONE'. Task completed!\"\n\n## Response Style\n\n- Be helpful and efficient\n- Always confirm actions taken\n- Ask clarifying questions when needed\n- Provide clear status updates\n- Use friendly, professional language\n- Show task details when relevant\n\n## Error Handling\n\n- If a task can't be found, offer to show all tasks\n- If status values are invalid, explain the valid options\n- If required information is missing, ask specific questions\n- Provide helpful suggestions when commands are unclear\n\nRemember: Always prioritize data accuracy and user confirmation, especially for destructive actions like deletion. Keep the task list organized and up-to-date."
}
},
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 2.2,
"position": [-384, -32],
"id": "e923a630-08b1-4e4d-a99c-23704085eaab",
"name": "Task management agent"
},
{
"parameters": {
"content": "## Start here!\n@[youtube](cmR6m_p0-ro)",
"height": 320,
"width": 464,
"color": 5
},
"type": "n8n-nodes-base.stickyNote",
"position": [-1200, -32],
"typeVersion": 1,
"id": "56870cd2-d303-41cf-8f8a-2bba13c3d3ea",
"name": "Sticky Note"
}
],
"pinData": {},
"connections": {
"Model": {
"ai_languageModel": [
[
{
"node": "Task management agent",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"When chat message received": {
"main": [
[
{
"node": "Task management agent",
"type": "main",
"index": 0
}
]
]
},
"Simple Memory": {
"ai_memory": [
[
{
"node": "Task management agent",
"type": "ai_memory",
"index": 0
}
]
]
},
"Get tasks": {
"ai_tool": [
[
{
"node": "Task management agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"Create task": {
"ai_tool": [
[
{
"node": "Task management agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"Update task": {
"ai_tool": [
[
{
"node": "Task management agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"Delete task": {
"ai_tool": [
[
{
"node": "Task management agent",
"type": "ai_tool",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1",
"saveDataSuccessExecution": "all",
"callerPolicy": "workflowsFromSameOwner"
},
"tags": []
}
@@ -1,276 +0,0 @@
{
"name": "Voice assistant agent",
"meta": {
"templateCredsSetupCompleted": false,
"templateId": "voice_assistant_agent_with_telegram"
},
"nodes": [
{
"parameters": {
"updates": ["message"],
"additionalFields": {
"download": false,
"userIds": "YOUR_USER_ID"
}
},
"id": "0c402bdc-7fa2-4fa1-96d6-f02853cfdaa1",
"name": "Telegram Message Trigger",
"type": "n8n-nodes-base.telegramTrigger",
"typeVersion": 1.2,
"position": [-192, 0],
"webhookId": "55acc711-c248-4ac9-b6cd-e295c2d33f4b",
"credentials": {}
},
{
"parameters": {
"sessionIdType": "customKey",
"sessionKey": "={{ $('Telegram Message Trigger').first().json.message.chat.id }}",
"contextWindowLength": 10
},
"id": "30bef40d-efbe-4920-acad-ce9a42c0061c",
"name": "Memory",
"type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
"typeVersion": 1.3,
"position": [752, 288]
},
{
"parameters": {
"chatId": "={{ $('Telegram Message Trigger').first().json.message.chat.id }}",
"text": "={{ $json.output }}",
"additionalFields": {}
},
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [1056, 0],
"id": "7dea5c17-bfdb-4874-918b-a6583298efa2",
"name": "Reply in Telegram",
"webhookId": "b96b7a41-9806-455f-b72e-00aa638eda71",
"credentials": {}
},
{
"parameters": {
"resource": "file",
"fileId": "={{ $json.message.voice.file_id }}",
"additionalFields": {}
},
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [256, -96],
"id": "66219c83-6371-4cc6-ad43-57cee44f52da",
"name": "Get a file",
"webhookId": "e0769871-efe3-49da-81a5-2f88a6fdd33f",
"credentials": {}
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "2b538077-d3d3-4713-b973-68748313ff97",
"leftValue": "={{ $json.message.voice }}",
"rightValue": "",
"operator": {
"type": "object",
"operation": "notEmpty",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [32, 0],
"id": "3a1a7f7f-f122-438d-a0f2-1e80d1072dba",
"name": "Check if Audio file"
},
{
"parameters": {
"resource": "audio",
"operation": "transcribe",
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.openAi",
"typeVersion": 1.8,
"position": [480, -96],
"id": "6f314aef-7ff1-4211-8656-e2c1f8fea374",
"name": "Transcribe audio",
"credentials": {}
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "eb912219-2436-4f04-8ffc-c1c20eb07344",
"name": "text",
"value": "={{ $json.message.text }}",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [480, 96],
"id": "2e9b0ca3-2a0f-441d-89a0-28365c5ba3ef",
"name": "Set field"
},
{
"parameters": {
"model": {
"__rl": true,
"value": "gpt-4.1-mini",
"mode": "list",
"cachedResultName": "gpt-4.1-mini"
},
"options": {}
},
"id": "61f3b0a0-210a-4eaa-91b5-e381d5f56754",
"name": "Model",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"typeVersion": 1.2,
"position": [624, 288],
"credentials": {}
},
{
"parameters": {
"promptType": "define",
"text": "={{ $json.text || $json.message.text }}",
"options": {
"systemMessage": "=# Telegram Personal AI Assistant System Prompt\n\nYou are a helpful personal AI assistant that communicates with users through Telegram.\n\n## Your Capabilities\n\nYou can help users with:\n- **General conversation** - Answer questions and provide information\n\n## Communication Style\n\n### Response Format\n- Keep responses conversational and friendly\n- Use appropriate length for Telegram (not too long)\n- Be helpful and informative\n- Match the user's tone and communication style\n\n### Voice Message Handling\n- When users send voice messages, respond naturally as if they spoke to you directly\n- Don't mention that their message was transcribed\n- Respond in a conversational way that flows naturally\n\n\n## Context Awareness\n\n- Remember the current date and time: **Today is {{ $now.format('cccc') }} the {{ $now.format('yyyy-MM-dd HH:mm') }}**\n- Use this information to provide relevant, timely responses\n- Reference \"today\", \"tomorrow\", \"this week\" appropriately\n\n## Important Guidelines\n\n- Maintain conversation context using the memory system\n- Ask clarifying questions when requests are ambiguous\n- Respect privacy - only access information when relevant to the request\n- If you can't help with something, suggest alternatives or redirect to your available capabilities\n\nRemember: You're a personal assistant, so be personable, helpful, and focused on making the user's day easier and more organized.\n\nToday is {{ $now.format('cccc') }} the {{ $now.format('yyyy-MM-dd HH:mm') }}."
}
},
"id": "f958d2f6-6602-4443-bfae-ef628af09b84",
"name": "Assistant Agent",
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 2.2,
"position": [704, 0]
},
{
"parameters": {
"content": "## Start here!\n@[youtube](aaOLJ2mUoVk)",
"height": 320,
"width": 464,
"color": 5
},
"type": "n8n-nodes-base.stickyNote",
"position": [-736, -80],
"typeVersion": 1,
"id": "cd6f0a09-148c-46e3-9858-11645de3a802",
"name": "Sticky Note1"
}
],
"connections": {
"Telegram Message Trigger": {
"main": [
[
{
"node": "Check if Audio file",
"type": "main",
"index": 0
}
]
]
},
"Memory": {
"ai_memory": [
[
{
"node": "Assistant Agent",
"type": "ai_memory",
"index": 0
}
]
]
},
"Get a file": {
"main": [
[
{
"node": "Transcribe audio",
"type": "main",
"index": 0
}
]
]
},
"Check if Audio file": {
"main": [
[
{
"node": "Get a file",
"type": "main",
"index": 0
}
],
[
{
"node": "Set field",
"type": "main",
"index": 0
}
]
]
},
"Transcribe audio": {
"main": [
[
{
"node": "Assistant Agent",
"type": "main",
"index": 0
}
]
]
},
"Set field": {
"main": [
[
{
"node": "Assistant Agent",
"type": "main",
"index": 0
}
]
]
},
"Model": {
"ai_languageModel": [
[
{
"node": "Assistant Agent",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Assistant Agent": {
"main": [
[
{
"node": "Reply in Telegram",
"type": "main",
"index": 0
}
]
]
}
},
"pinData": {},
"active": false,
"settings": {
"executionOrder": "v1"
},
"tags": []
}
@@ -4,12 +4,6 @@ import { isWorkflowDataWithTemplateId } from './typeGuards';
/* eslint-disable import-x/extensions */
import easyAiStarterJson from './samples/easy_ai_starter.json';
import ragStarterJson from './samples/rag_starter.json';
import emailTriageAgentWithGmailJson from './samples/agents/email_triage_agent_with_gmail.json';
import jokeAgentWithHttpToolJson from './samples/agents/joke_agent_with_http_tool.json';
import knowledgeStoreAgentWithGoogleDriveJson from './samples/agents/knowledge_store_agent_with_google_drive.json';
import taskManagementAgentWithGoogleSheetsJson from './samples/agents/task_management_agent_with_google_sheets.json';
import voiceAssistantAgentJson from './samples/agents/voice-agent.json';
import calendarAgentJson from './samples/agents/calendar-agent.json';
import buildYourFirstAiAgentJson from './samples/tutorial/build_your_first_ai_agent.json';
import jsonBasicsJson from './samples/tutorial/json_basics.json';
import expressionsTutorialJson from './samples/tutorial/expressions_tutorial.json';
@@ -38,15 +32,6 @@ export const SampleTemplates = {
EasyAiTemplate: getEasyAiWorkflowJson().meta.templateId,
} as const;
export const PrebuiltAgentTemplates = {
CalendarAgent: getWorkflowJson(calendarAgentJson).meta.templateId,
EmailTriageAgent: getWorkflowJson(emailTriageAgentWithGmailJson).meta.templateId,
KnowledgeStoreAgent: getWorkflowJson(knowledgeStoreAgentWithGoogleDriveJson).meta.templateId,
TaskManagementAgent: getWorkflowJson(taskManagementAgentWithGoogleSheetsJson).meta.templateId,
JokeAgent: getWorkflowJson(jokeAgentWithHttpToolJson).meta.templateId,
VoiceAssistantAgent: getWorkflowJson(voiceAssistantAgentJson).meta.templateId,
} as const;
export const TutorialTemplates = {
BuildYourFirstAiAgent: getWorkflowJson(buildYourFirstAiAgentJson).meta.templateId,
JsonBasics: getWorkflowJson(jsonBasicsJson).meta.templateId,
@@ -55,10 +40,6 @@ export const TutorialTemplates = {
ApiFundamentals: getWorkflowJson(apiFundamentalsJson).meta.templateId,
} as const;
export const isPrebuiltAgentTemplateId = (value: string): boolean => {
return Object.values(PrebuiltAgentTemplates).includes(value);
};
export const isTutorialTemplateId = (value: string): boolean => {
return Object.values(TutorialTemplates).includes(value);
};
@@ -70,81 +51,6 @@ interface SampleTemplate {
nodes: INodeTypeNameVersion[];
}
export const getPrebuiltAgents = (): SampleTemplate[] => {
return [
{
name: 'Voice assistant agent',
description: 'Personal AI assistant in Telegram, handling both text and voice messages.',
template: getWorkflowJson(voiceAssistantAgentJson),
nodes: [
{
name: 'n8n-nodes-base.telegram',
version: 1.2,
},
],
},
{
name: 'Email triage agent',
description:
'Categorizes new, unread emails by analyzing their content and applying relevant labels.',
template: getWorkflowJson(emailTriageAgentWithGmailJson),
nodes: [
{
name: 'n8n-nodes-base.gmail',
version: 2.1,
},
],
},
{
name: 'Knowledge store agent',
description:
'Retrieve, analyze, and answer questions using documents uploaded to Google Drive.',
template: getWorkflowJson(knowledgeStoreAgentWithGoogleDriveJson),
nodes: [
{
name: 'n8n-nodes-base.googleDrive',
version: 3,
},
],
},
{
name: 'Calendar agent',
description:
'Agent that can interact with your Google calendar to get availability and a list of events.',
template: getWorkflowJson(calendarAgentJson),
nodes: [
{
name: 'n8n-nodes-base.googleCalendar',
version: 1.3,
},
],
},
{
name: 'Task management agent',
description:
'Task management assistant that helps users create, view, update, and delete tasks.',
template: getWorkflowJson(taskManagementAgentWithGoogleSheetsJson),
nodes: [
{
name: 'n8n-nodes-base.googleSheets',
version: 4.7,
},
],
},
{
name: 'Joke agent',
description: 'Uses the Joke API via the HTTP tool to deliver fun, personalized jokes.',
template: getWorkflowJson(jokeAgentWithHttpToolJson),
nodes: [
{
name: 'n8n-nodes-base.httpRequest',
version: 4.2,
},
],
},
];
};
export const getTutorialTemplates = (): SampleTemplate[] => {
return [
{
@@ -191,7 +97,6 @@ export const getSampleWorkflowByTemplateId = (
const workflows = [
getEasyAiWorkflowJson(),
getRagStarterWorkflowJson(),
...getPrebuiltAgents().map((agent) => agent.template),
...getTutorialTemplates().map((tutorial) => tutorial.template),
];
@@ -9,7 +9,6 @@ import type {
INodeTypeDescription,
JsonObject,
NodeExecutionHint,
INodeProperties,
} from 'n8n-workflow';
import { NodeConnectionTypes, NodeApiError, NodeOperationError } from 'n8n-workflow';
import { v4 as uuid } from 'uuid';
@@ -32,22 +31,6 @@ import {
} from './GenericFunctions';
import { sortItemKeysByPriorityList } from '../../../utils/utilities';
const preBuiltAgentsCallout: INodeProperties = {
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased
displayName: 'Interact with your Google Calendar using our pre-built',
name: 'preBuiltAgentsCalloutGoogleCalendar',
type: 'callout',
typeOptions: {
calloutAction: {
label: 'Calendar agent',
icon: 'bot',
type: 'openSampleWorkflowTemplate',
templateId: 'calendar_agent_with_gcal',
},
},
default: '',
};
export class GoogleCalendar implements INodeType {
description: INodeTypeDescription = {
displayName: 'Google Calendar',
@@ -70,7 +53,6 @@ export class GoogleCalendar implements INodeType {
},
],
properties: [
preBuiltAgentsCallout,
{
displayName: 'Resource',
name: 'resource',
@@ -1,28 +1,11 @@
/* eslint-disable n8n-nodes-base/node-filename-against-convention */
import { NodeConnectionTypes, type INodeTypeDescription, type INodeProperties } from 'n8n-workflow';
import { NodeConnectionTypes, type INodeTypeDescription } from 'n8n-workflow';
import * as drive from './drive/Drive.resource';
import * as file from './file/File.resource';
import * as fileFolder from './fileFolder/FileFolder.resource';
import * as folder from './folder/Folder.resource';
const preBuiltAgentsCallout: INodeProperties = {
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased
displayName:
'Retrieve, analyze, and answer questions using your Google Drive documents with our pre-built',
name: 'preBuiltAgentsCalloutGoogleDrive',
type: 'callout',
typeOptions: {
calloutAction: {
label: 'Knowledge store agent',
icon: 'bot',
type: 'openSampleWorkflowTemplate',
templateId: 'knowledge_store_agent_with_google_drive',
},
},
default: '',
};
export const versionDescription: INodeTypeDescription = {
displayName: 'Google Drive',
name: 'googleDrive',
@@ -58,7 +41,6 @@ export const versionDescription: INodeTypeDescription = {
},
],
properties: [
preBuiltAgentsCallout,
{
displayName: 'Authentication',
name: 'authentication',
@@ -2,7 +2,6 @@ import type {
IDataObject,
IExecuteFunctions,
INodeExecutionData,
INodeProperties,
INodeType,
INodeTypeBaseDescription,
INodeTypeDescription,
@@ -38,22 +37,6 @@ import {
} from '../GenericFunctions';
import { replyToEmail } from '../utils/replyToEmail';
const preBuiltAgentsCallout: INodeProperties = {
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased
displayName: 'Sort your Gmail inbox using our pre-built',
name: 'preBuiltAgentsCalloutGmail',
type: 'callout',
typeOptions: {
calloutAction: {
label: 'Email triage agent',
icon: 'bot',
type: 'openSampleWorkflowTemplate',
templateId: 'email_triage_agent_with_gmail',
},
},
default: '',
};
const versionDescription: INodeTypeDescription = {
displayName: 'Gmail',
name: 'gmail',
@@ -91,7 +74,6 @@ const versionDescription: INodeTypeDescription = {
waitingNodeTooltip: SEND_AND_WAIT_WAITING_TOOLTIP,
webhooks: sendAndWaitWebhooksDescription,
properties: [
preBuiltAgentsCallout,
{
displayName: 'Authentication',
name: 'authentication',
@@ -23,22 +23,6 @@ export const authentication: INodeProperties = {
default: 'oAuth2',
};
const preBuiltAgentsCallout: INodeProperties = {
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased
displayName: 'Manage tasks in Google Sheets using our pre-built',
name: 'preBuiltAgentsCalloutGoogleSheets',
type: 'callout',
typeOptions: {
calloutAction: {
label: 'Task management agent',
icon: 'bot',
type: 'openSampleWorkflowTemplate',
templateId: 'task_management_agent_with_google_sheets',
},
},
default: '',
};
export const versionDescription: INodeTypeDescription = {
displayName: 'Google Sheets',
name: 'googleSheets',
@@ -110,7 +94,6 @@ export const versionDescription: INodeTypeDescription = {
},
],
properties: [
preBuiltAgentsCallout,
authentication,
{
displayName: 'Resource',
@@ -2,24 +2,7 @@ import type { INodeProperties } from 'n8n-workflow';
import { optimizeResponseProperties } from '../shared/optimizeResponse';
const preBuiltAgentsCallout: INodeProperties = {
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased
displayName: 'Try the HTTP request tool with our pre-built',
name: 'preBuiltAgentsCalloutHttpRequest',
type: 'callout',
typeOptions: {
calloutAction: {
label: 'Joke agent',
icon: 'bot',
type: 'openSampleWorkflowTemplate',
templateId: 'joke_agent_with_http_tool',
},
},
default: '',
};
export const mainProperties: INodeProperties[] = [
preBuiltAgentsCallout,
{
displayName: '',
name: 'curlImport',
@@ -6,7 +6,6 @@ import type {
INodeType,
INodeTypeDescription,
IHttpRequestMethods,
INodeProperties,
} from 'n8n-workflow';
import {
BINARY_ENCODING,
@@ -31,22 +30,6 @@ import {
sendAndWaitWebhook,
} from '../../utils/sendAndWait/utils';
const preBuiltAgentsCallout: INodeProperties = {
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased
displayName: 'Interact with Telegram using our pre-built',
name: 'preBuiltAgentsCalloutTelegram',
type: 'callout',
typeOptions: {
calloutAction: {
label: 'Voice assistant agent',
icon: 'bot',
type: 'openSampleWorkflowTemplate',
templateId: 'voice_assistant_agent_with_telegram',
},
},
default: '',
};
export class Telegram implements INodeType {
description: INodeTypeDescription = {
displayName: 'Telegram',
@@ -71,7 +54,6 @@ export class Telegram implements INodeType {
waitingNodeTooltip: SEND_AND_WAIT_WAITING_TOOLTIP,
webhooks: sendAndWaitWebhooksDescription,
properties: [
preBuiltAgentsCallout,
{
displayName: 'Resource',
name: 'resource',
+1 -7
View File
@@ -1439,18 +1439,12 @@ export interface CalloutActionBase {
icon?: string;
}
export interface CalloutActionOpenPreBuiltAgentsCollection extends CalloutActionBase {
type: 'openPreBuiltAgentsCollection';
}
export interface CalloutActionOpenSampleWorkflowTemplate extends CalloutActionBase {
type: 'openSampleWorkflowTemplate';
templateId: string;
}
export type CalloutAction =
| CalloutActionOpenPreBuiltAgentsCollection
| CalloutActionOpenSampleWorkflowTemplate;
export type CalloutAction = CalloutActionOpenSampleWorkflowTemplate;
export interface INodePropertyTypeOptions {
// Supported by: button