mirror of
https://github.com/czlonkowski/n8n-mcp.git
synced 2026-01-30 06:22:04 +00:00
177 lines
7.6 KiB
JavaScript
177 lines
7.6 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.mutationTracker = exports.MutationTracker = void 0;
|
|
const intent_classifier_js_1 = require("./intent-classifier.js");
|
|
const mutation_validator_js_1 = require("./mutation-validator.js");
|
|
const intent_sanitizer_js_1 = require("./intent-sanitizer.js");
|
|
const workflow_sanitizer_js_1 = require("./workflow-sanitizer.js");
|
|
const logger_js_1 = require("../utils/logger.js");
|
|
class MutationTracker {
|
|
constructor() {
|
|
this.recentMutations = [];
|
|
this.RECENT_MUTATIONS_LIMIT = 100;
|
|
}
|
|
async processMutation(data, userId) {
|
|
try {
|
|
if (!this.validateMutationData(data)) {
|
|
logger_js_1.logger.debug('Mutation data validation failed');
|
|
return null;
|
|
}
|
|
const workflowBefore = workflow_sanitizer_js_1.WorkflowSanitizer.sanitizeWorkflowRaw(data.workflowBefore);
|
|
const workflowAfter = workflow_sanitizer_js_1.WorkflowSanitizer.sanitizeWorkflowRaw(data.workflowAfter);
|
|
const sanitizedIntent = intent_sanitizer_js_1.intentSanitizer.sanitize(data.userIntent);
|
|
if (mutation_validator_js_1.mutationValidator.shouldExclude(data)) {
|
|
logger_js_1.logger.debug('Mutation excluded from tracking based on quality criteria');
|
|
return null;
|
|
}
|
|
if (mutation_validator_js_1.mutationValidator.isDuplicate(workflowBefore, workflowAfter, data.operations, this.recentMutations)) {
|
|
logger_js_1.logger.debug('Duplicate mutation detected, skipping tracking');
|
|
return null;
|
|
}
|
|
const hashBefore = mutation_validator_js_1.mutationValidator.hashWorkflow(workflowBefore);
|
|
const hashAfter = mutation_validator_js_1.mutationValidator.hashWorkflow(workflowAfter);
|
|
const structureHashBefore = workflow_sanitizer_js_1.WorkflowSanitizer.generateWorkflowHash(workflowBefore);
|
|
const structureHashAfter = workflow_sanitizer_js_1.WorkflowSanitizer.generateWorkflowHash(workflowAfter);
|
|
const intentClassification = intent_classifier_js_1.intentClassifier.classify(data.operations, sanitizedIntent);
|
|
const changeMetrics = this.calculateChangeMetrics(data.operations);
|
|
const validationMetrics = this.calculateValidationMetrics(data.validationBefore, data.validationAfter);
|
|
const record = {
|
|
userId,
|
|
sessionId: data.sessionId,
|
|
workflowBefore,
|
|
workflowAfter,
|
|
workflowHashBefore: hashBefore,
|
|
workflowHashAfter: hashAfter,
|
|
workflowStructureHashBefore: structureHashBefore,
|
|
workflowStructureHashAfter: structureHashAfter,
|
|
userIntent: sanitizedIntent,
|
|
intentClassification,
|
|
toolName: data.toolName,
|
|
operations: data.operations,
|
|
operationCount: data.operations.length,
|
|
operationTypes: this.extractOperationTypes(data.operations),
|
|
validationBefore: data.validationBefore,
|
|
validationAfter: data.validationAfter,
|
|
...validationMetrics,
|
|
...changeMetrics,
|
|
mutationSuccess: data.mutationSuccess,
|
|
mutationError: data.mutationError,
|
|
durationMs: data.durationMs,
|
|
};
|
|
this.addToRecentMutations(hashBefore, hashAfter, data.operations);
|
|
return record;
|
|
}
|
|
catch (error) {
|
|
logger_js_1.logger.error('Error processing mutation:', error);
|
|
return null;
|
|
}
|
|
}
|
|
validateMutationData(data) {
|
|
const validationResult = mutation_validator_js_1.mutationValidator.validate(data);
|
|
if (!validationResult.valid) {
|
|
logger_js_1.logger.warn('Mutation data validation failed:', validationResult.errors);
|
|
return false;
|
|
}
|
|
if (validationResult.warnings.length > 0) {
|
|
logger_js_1.logger.debug('Mutation data validation warnings:', validationResult.warnings);
|
|
}
|
|
return true;
|
|
}
|
|
calculateChangeMetrics(operations) {
|
|
const metrics = {
|
|
nodesAdded: 0,
|
|
nodesRemoved: 0,
|
|
nodesModified: 0,
|
|
connectionsAdded: 0,
|
|
connectionsRemoved: 0,
|
|
propertiesChanged: 0,
|
|
};
|
|
for (const op of operations) {
|
|
switch (op.type) {
|
|
case 'addNode':
|
|
metrics.nodesAdded++;
|
|
break;
|
|
case 'removeNode':
|
|
metrics.nodesRemoved++;
|
|
break;
|
|
case 'updateNode':
|
|
metrics.nodesModified++;
|
|
if ('updates' in op && op.updates) {
|
|
metrics.propertiesChanged += Object.keys(op.updates).length;
|
|
}
|
|
break;
|
|
case 'addConnection':
|
|
metrics.connectionsAdded++;
|
|
break;
|
|
case 'removeConnection':
|
|
metrics.connectionsRemoved++;
|
|
break;
|
|
case 'rewireConnection':
|
|
metrics.connectionsRemoved++;
|
|
metrics.connectionsAdded++;
|
|
break;
|
|
case 'replaceConnections':
|
|
if ('connections' in op && op.connections) {
|
|
metrics.connectionsRemoved++;
|
|
metrics.connectionsAdded++;
|
|
}
|
|
break;
|
|
case 'updateSettings':
|
|
if ('settings' in op && op.settings) {
|
|
metrics.propertiesChanged += Object.keys(op.settings).length;
|
|
}
|
|
break;
|
|
case 'moveNode':
|
|
case 'enableNode':
|
|
case 'disableNode':
|
|
case 'updateName':
|
|
case 'addTag':
|
|
case 'removeTag':
|
|
case 'activateWorkflow':
|
|
case 'deactivateWorkflow':
|
|
case 'cleanStaleConnections':
|
|
metrics.propertiesChanged++;
|
|
break;
|
|
}
|
|
}
|
|
return metrics;
|
|
}
|
|
calculateValidationMetrics(validationBefore, validationAfter) {
|
|
if (!validationBefore || !validationAfter) {
|
|
return {
|
|
validationImproved: null,
|
|
errorsResolved: 0,
|
|
errorsIntroduced: 0,
|
|
};
|
|
}
|
|
const errorsBefore = validationBefore.errors?.length || 0;
|
|
const errorsAfter = validationAfter.errors?.length || 0;
|
|
const errorsResolved = Math.max(0, errorsBefore - errorsAfter);
|
|
const errorsIntroduced = Math.max(0, errorsAfter - errorsBefore);
|
|
const validationImproved = errorsBefore > errorsAfter;
|
|
return {
|
|
validationImproved,
|
|
errorsResolved,
|
|
errorsIntroduced,
|
|
};
|
|
}
|
|
extractOperationTypes(operations) {
|
|
const types = new Set(operations.map((op) => op.type));
|
|
return Array.from(types);
|
|
}
|
|
addToRecentMutations(hashBefore, hashAfter, operations) {
|
|
this.recentMutations.push({ hashBefore, hashAfter, operations });
|
|
if (this.recentMutations.length > this.RECENT_MUTATIONS_LIMIT) {
|
|
this.recentMutations.shift();
|
|
}
|
|
}
|
|
clearRecentMutations() {
|
|
this.recentMutations = [];
|
|
}
|
|
getRecentMutationsCount() {
|
|
return this.recentMutations.length;
|
|
}
|
|
}
|
|
exports.MutationTracker = MutationTracker;
|
|
exports.mutationTracker = new MutationTracker();
|
|
//# sourceMappingURL=mutation-tracker.js.map
|