refactor: createVectorStoreNode refactoring and embeddings batching support (no-changelog) (#13674)

This commit is contained in:
oleg
2025-03-13 16:23:06 +01:00
committed by GitHub
parent 5b6b78709e
commit b4672b8deb
34 changed files with 2359 additions and 566 deletions

View File

@@ -14,6 +14,18 @@ import {
defaultNodeDescriptions,
} from '@/__tests__/mocks';
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
import * as lodash from 'lodash-es';
vi.mock('lodash-es', async () => {
const actual = await vi.importActual('lodash-es');
return {
...actual,
debounce: vi.fn((fn) => {
// Return a function that immediately calls the provided function
return (...args: unknown[]) => fn(...args);
}),
};
});
const renderComponent = createComponentRenderer(WorkflowCanvas, {
props: {
@@ -143,4 +155,52 @@ describe('WorkflowCanvas', () => {
expect(container.querySelector(`[data-id="${nodes[0].id}"]`)).toBeInTheDocument();
expect(container.querySelector(`[data-id="${fallbackNodes[0].id}"]`)).not.toBeInTheDocument();
});
describe('debouncing behavior', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('should initialize debounced watchers on component mount', async () => {
renderComponent();
expect(lodash.debounce).toHaveBeenCalledTimes(3);
});
it('should configure debouncing with no delay when not executing', async () => {
renderComponent({
props: {
executing: false,
},
});
expect(lodash.debounce).toHaveBeenCalledTimes(3);
// Find calls related to our specific debouncing logic
const calls = vi.mocked(lodash.debounce).mock.calls;
const nonExecutingCalls = calls.filter((call) => call[1] === 0 && call[2]?.maxWait === 0);
expect(nonExecutingCalls.length).toBeGreaterThanOrEqual(2);
expect(nonExecutingCalls[0][1]).toBe(0);
expect(nonExecutingCalls[0][2]).toEqual({ maxWait: 0 });
});
it('should configure debouncing with delay when executing', async () => {
renderComponent({
props: {
executing: true,
},
});
expect(lodash.debounce).toHaveBeenCalledTimes(3);
// Find calls related to our specific debouncing logic
const calls = vi.mocked(lodash.debounce).mock.calls;
const executingCalls = calls.filter((call) => call[1] === 200 && call[2]?.maxWait === 50);
expect(executingCalls.length).toBeGreaterThanOrEqual(2);
expect(executingCalls[0][1]).toBe(200);
expect(executingCalls[0][2]).toEqual({ maxWait: 50 });
});
});
});

View File

@@ -1,13 +1,15 @@
<script setup lang="ts">
import Canvas from '@/components/canvas/Canvas.vue';
import { computed, ref, toRef, useCssModule } from 'vue';
import type { WatchStopHandle } from 'vue';
import { computed, ref, toRef, useCssModule, watch } from 'vue';
import type { Workflow } from 'n8n-workflow';
import type { IWorkflowDb } from '@/Interface';
import { useCanvasMapping } from '@/composables/useCanvasMapping';
import type { EventBus } from '@n8n/utils/event-bus';
import { createEventBus } from '@n8n/utils/event-bus';
import type { CanvasEventBusEvents } from '@/types';
import type { CanvasConnection, CanvasEventBusEvents, CanvasNode } from '@/types';
import { useVueFlow } from '@vue-flow/core';
import { debounce } from 'lodash-es';
defineOptions({
inheritAttrs: false,
@@ -59,6 +61,57 @@ onNodesInitialized(() => {
initialFitViewDone.value = true;
}
});
// Debounced versions of nodes and connections and watchers
const nodesDebounced = ref<CanvasNode[]>([]);
const connectionsDebounced = ref<CanvasConnection[]>([]);
const debounceNodesWatcher = ref<WatchStopHandle>();
const debounceConnectionsWatcher = ref<WatchStopHandle>();
// Update debounce watchers when execution state changes
watch(() => props.executing, setupDebouncedWatchers, { immediate: true });
/**
* Sets up debounced watchers for nodes and connections
* Uses different debounce times based on execution state:
* - During execution: Debounce updates to reduce performance impact for large number of nodes/items
* - Otherwise: Update immediately
*/
function setupDebouncedWatchers() {
// Clear existing watchers if they exist
debounceNodesWatcher.value?.();
debounceConnectionsWatcher.value?.();
// Configure debounce parameters based on execution state
const debounceTime = props.executing ? 200 : 0;
const maxWait = props.executing ? 50 : 0;
// Set up debounced watcher for nodes
debounceNodesWatcher.value = watch(
mappedNodes,
debounce(
(value) => {
nodesDebounced.value = value;
},
debounceTime,
{ maxWait },
),
{ immediate: true, deep: true },
);
// Set up debounced watcher for connections
debounceConnectionsWatcher.value = watch(
mappedConnections,
debounce(
(value) => {
connectionsDebounced.value = value;
},
debounceTime,
{ maxWait },
),
{ immediate: true, deep: true },
);
}
</script>
<template>
@@ -67,8 +120,8 @@ onNodesInitialized(() => {
<Canvas
v-if="workflow"
:id="id"
:nodes="mappedNodes"
:connections="mappedConnections"
:nodes="nodesDebounced"
:connections="connectionsDebounced"
:event-bus="eventBus"
:read-only="readOnly"
v-bind="$attrs"