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',
|
name: 'binaryProperty',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
default: 'data',
|
default: 'data',
|
||||||
displayOptions: { show: { source: ['binary'] } },
|
description:
|
||||||
description: 'Name of the binary property that holds the image',
|
'Name of the binary property that holds the image. Used when Source is Binary File.',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Image URL',
|
displayName: 'Image URL',
|
||||||
|
|
@ -399,8 +399,8 @@ export class Teamorouter implements INodeType {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
default: '',
|
default: '',
|
||||||
placeholder: 'https://example.com/cat.png',
|
placeholder: 'https://example.com/cat.png',
|
||||||
displayOptions: { show: { source: ['url'] } },
|
description:
|
||||||
description: 'Publicly reachable image link, or a data:image/...;base64,... URL',
|
'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',
|
name: 'inputFidelity',
|
||||||
type: 'options',
|
type: 'options',
|
||||||
default: 'high',
|
default: 'high',
|
||||||
displayOptions: { show: { operation: ['edit'] } },
|
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
name: 'High',
|
name: 'High',
|
||||||
|
|
@ -609,19 +608,18 @@ export class Teamorouter implements INodeType {
|
||||||
description: 'Allow the model to reinterpret the source image more freely',
|
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',
|
displayName: 'Moderation',
|
||||||
name: 'moderation',
|
name: 'moderation',
|
||||||
type: 'options',
|
type: 'options',
|
||||||
default: 'auto',
|
default: 'auto',
|
||||||
displayOptions: { show: { operation: ['generate'] } },
|
|
||||||
options: [
|
options: [
|
||||||
{ name: 'Auto', value: 'auto', description: 'Standard content filter' },
|
{ name: 'Auto', value: 'auto', description: 'Standard content filter' },
|
||||||
{ name: 'Low', value: 'low', description: 'Relaxed 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',
|
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;
|
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.deepEqual(chatModel.description.inputs, [], 'chat model sub-node must not have a main input');
|
||||||
assert.ok(
|
assert.ok(
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue