Keep displayOptions out of collection children
n8n resolves every parameter against its displayOptions when it builds a Workflow, before any execution exists. A displayOptions on a child of a collection or fixedCollection made that resolution loop, so image runs answered HTTP 500 in under a second and left no execution record at all. Drop those four displayOptions and say in each description when the field applies, then build the Workflow twice in the validation test - with every collection present but empty - so this cannot come back.
This commit is contained in:
parent
777ca4d749
commit
e490c1dc83
2 changed files with 45 additions and 8 deletions
|
|
@ -390,8 +390,8 @@ export class Teamorouter implements INodeType {
|
|||
name: 'binaryProperty',
|
||||
type: 'string',
|
||||
default: 'data',
|
||||
displayOptions: { show: { source: ['binary'] } },
|
||||
description: 'Name of the binary property that holds the image',
|
||||
description:
|
||||
'Name of the binary property that holds the image. Used when Source is Binary File.',
|
||||
},
|
||||
{
|
||||
displayName: 'Image URL',
|
||||
|
|
@ -399,8 +399,8 @@ export class Teamorouter implements INodeType {
|
|||
type: 'string',
|
||||
default: '',
|
||||
placeholder: 'https://example.com/cat.png',
|
||||
displayOptions: { show: { source: ['url'] } },
|
||||
description: 'Publicly reachable image link, or a data:image/...;base64,... URL',
|
||||
description:
|
||||
'Publicly reachable image link, or a data:image/...;base64,... URL. Used when Source is Image URL.',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
@ -596,7 +596,6 @@ export class Teamorouter implements INodeType {
|
|||
name: 'inputFidelity',
|
||||
type: 'options',
|
||||
default: 'high',
|
||||
displayOptions: { show: { operation: ['edit'] } },
|
||||
options: [
|
||||
{
|
||||
name: 'High',
|
||||
|
|
@ -609,19 +608,18 @@ export class Teamorouter implements INodeType {
|
|||
description: 'Allow the model to reinterpret the source image more freely',
|
||||
},
|
||||
],
|
||||
description: 'How closely the edit follows the source image',
|
||||
description: 'How closely the edit follows the source image. Only used when editing an image.',
|
||||
},
|
||||
{
|
||||
displayName: 'Moderation',
|
||||
name: 'moderation',
|
||||
type: 'options',
|
||||
default: 'auto',
|
||||
displayOptions: { show: { operation: ['generate'] } },
|
||||
options: [
|
||||
{ name: 'Auto', value: 'auto', description: 'Standard content filter' },
|
||||
{ name: 'Low', value: 'low', description: 'Relaxed content filter' },
|
||||
],
|
||||
description: 'Content filter applied by TeamoRouter',
|
||||
description: 'Content filter applied by TeamoRouter. Only used when generating an image.',
|
||||
},
|
||||
{
|
||||
displayName: 'Number of Images',
|
||||
|
|
|
|||
|
|
@ -105,6 +105,45 @@ for (const { file, instance } of loaded.nodes) {
|
|||
}
|
||||
}
|
||||
|
||||
// n8n builds a Workflow object — resolving every parameter against its displayOptions — before an
|
||||
// execution exists, and it builds it again from the parameters it wrote back. If that resolution
|
||||
// loops the webhook request dies with "There was a problem executing the workflow" and no run at all.
|
||||
// Building with every collection present but empty is the shape that used to break.
|
||||
const { Workflow } = require('n8n-workflow');
|
||||
|
||||
for (const { instance } of loaded.nodes) {
|
||||
const containers = instance.description.properties
|
||||
.filter((property) => property.type === 'collection' || property.type === 'fixedCollection')
|
||||
.map((property) => property.name);
|
||||
const resources = instance.description.properties.find((property) => property.name === 'resource')
|
||||
?.options ?? [{ value: undefined }];
|
||||
|
||||
const build = (parameters) =>
|
||||
new Workflow({
|
||||
id: 'validation',
|
||||
nodes: [
|
||||
{
|
||||
id: 'validation-node',
|
||||
name: instance.description.displayName,
|
||||
type: instance.description.name,
|
||||
typeVersion: instance.description.version,
|
||||
position: [0, 0],
|
||||
parameters: JSON.parse(JSON.stringify(parameters)),
|
||||
},
|
||||
],
|
||||
connections: {},
|
||||
active: false,
|
||||
nodeTypes: { getByNameAndVersion: () => instance, getByName: () => instance },
|
||||
});
|
||||
|
||||
for (const { value: resource } of resources) {
|
||||
const parameters = { resource, ...Object.fromEntries(containers.map((name) => [name, {}])) };
|
||||
// The parameters of the first pass are the ones n8n stores and reads back.
|
||||
const firstPass = build(parameters);
|
||||
build(firstPass.nodes[instance.description.displayName].parameters);
|
||||
}
|
||||
}
|
||||
|
||||
const chatModel = loaded.nodes.find(({ instance }) => instance.description.name === 'lmChatTeamRouter').instance;
|
||||
assert.deepEqual(chatModel.description.inputs, [], 'chat model sub-node must not have a main input');
|
||||
assert.ok(
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue