n8n/packages/@n8n/instance-ai/evaluations/binaryChecks/checks/valid-node-config.ts
Benjamin Schroth c961849226
feat(ai-builder): Add sub-agent evaluation harness with binary checks (no-changelog) (#28289)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 07:50:46 +00:00

69 lines
2.5 KiB
TypeScript

import { validateWorkflow } from '@n8n/workflow-sdk';
import type { WorkflowJSON } from '@n8n/workflow-sdk';
import type { WorkflowResponse } from '../../clients/n8n-client';
import type { BinaryCheck } from '../types';
/** Error codes emitted by schema validation in validateWorkflow */
const SCHEMA_ERROR_CODES = new Set(['INVALID_PARAMETER', 'MISSING_PARAMETER']);
/**
* WorkflowResponse uses `Record<string, unknown>` for parameters/connections where
* WorkflowJSON uses narrower type aliases (IDataObject, IConnections). The shapes
* are structurally compatible at runtime but require a bridge cast at compile time.
*
* If either type drifts (e.g. WorkflowJSON gains a required field), this cast
* will silently hide the mismatch. If that happens, replace the `as unknown as`
* with an explicit per-field mapper and let TS enforce the shape.
*/
function toWorkflowJson(workflow: WorkflowResponse): WorkflowJSON {
return {
name: workflow.name,
nodes: (workflow.nodes ?? []).map((n, i) => ({
id: String(i),
name: n.name,
type: n.type,
typeVersion: n.typeVersion ?? 1,
parameters: n.parameters ?? {},
position: [0, 0],
})),
connections: workflow.connections,
} as unknown as WorkflowJSON;
}
/**
* Validates node parameters against generated Zod schemas via workflow-sdk.
*
* Covers both:
* - Missing required parameters (what ai-workflow-builder.ee checked as `valid_required_parameters`)
* - Invalid option values (what ai-workflow-builder.ee checked as `valid_options_values`)
*
* Uses `validateWorkflow` with trigger/connectivity checks disabled (handled by
* other binary checks), keeping only schema validation. Gracefully skips nodes
* that have no schema available.
*/
export const validNodeConfig: BinaryCheck = {
name: 'valid_node_config',
description: 'Node parameters match their Zod schemas (required params, valid options)',
kind: 'deterministic',
run(workflow: WorkflowResponse) {
const nodes = workflow.nodes ?? [];
if (nodes.length === 0) return { pass: true };
const result = validateWorkflow(toWorkflowJson(workflow), {
allowNoTrigger: true,
allowDisconnectedNodes: true,
validateSchema: true,
});
// Only report schema-level errors, not structural warnings
const schemaErrors = result.errors.filter((e) => SCHEMA_ERROR_CODES.has(e.code));
if (schemaErrors.length === 0) return { pass: true };
const messages = schemaErrors.map((e) => `${e.nodeName ? `${e.nodeName}: ` : ''}${e.message}`);
return { pass: false, comment: messages.join('; ') };
},
};