Conversation
…roundtrip
The `core_metadata` node is configured with pydantic `extra='allow'` and
carries recall metadata (`loras`, `controlnets`, `ipAdapters`,
`t2iAdapters`, plus model-specific extras like `z_image_seed_variance_*`,
`dype_preset`, `ref_images`). When a generated image's graph was loaded
into the workflow editor and saved, those values were being dropped:
* The four `*MetadataField` collection types were not registered as
StatefulFieldType, so their inputs fell through to
`zStatelessFieldInputInstance`, whose `value` is
`z.undefined().catch(undefined)` — silently coercing the array away.
* Extra keys not declared in the OpenAPI schema were dropped earlier
still, in `graphToWorkflow`, because `template.inputs[key]` was
undefined and the field was skipped with a warning.
Result: the regenerated image had no recall metadata and "Recall all
parameters" reported no LoRA / no variance settings — even though the
backend executed the workflow correctly (`lora_selector` and the
`z_image_seed_variance_enhancer` node were intact via their edges).
This change:
* Registers `LoRAMetadataField`, `ControlNetMetadataField`,
`IPAdapterMetadataField`, `T2IAdapterMetadataField` as stateful field
types with passthrough zod values.
* Adds a synthetic `MetadataExtraField` so undeclared keys on
`extra='allow'` nodes round-trip through the workflow editor.
* `graphToWorkflow` synthesizes an extra-field template for keys not in
the node template, scoped to nodes that accept extras.
* `buildNodesGraph` forwards extra values verbatim when running the
workflow.
* `fieldValidators` and `InputFieldGate` no longer treat undeclared
inputs on extra-accepting nodes as errors / unexpected fields.
Adds regression tests covering the LoRA roundtrip, the extras roundtrip,
and the parseSchema template type for `core_metadata.loras`.
Fixes invoke-ai#9151
JPPhoto
left a comment
There was a problem hiding this comment.
-
invokeai/frontend/web/src/features/nodes/types/field.ts:1334MetadataExtraFieldusesz.any()and is added to the globalzFieldInputInstanceunion. Workflow input instances do not include the field template/type when parsed; they are parsed asz.record(z.string(), zFieldInputInstance)atinvokeai/frontend/web/src/features/nodes/types/invocation.ts:34. Because of that, this new catch-all input instance can match any malformed input value anywhere in any workflow, not justcore_metadataextras.Before this PR, a stateless connection-only input with an accidental saved value would fall through to
zStatelessFieldValue, which coerces toundefinedat invokeai/frontend/web/src/features/nodes/types/field.ts:1362. After this PR, that same value can be preserved by theMetadataExtraFieldbranch. ThenbuildNodesGraphserializesinput.valuefor every known template atinvokeai/frontend/web/src/features/nodes/util/graph/buildNodesGraph.ts:75. This lets stale or malformed workflow data become backend graph input data for fields that are supposed to be connection-only.Load or import a workflow with a non-
core_metadatanode whose stateless/connection-only input has a saved value. The parser accepts the value, and enqueue/export can send it into the backend graph.To expose this issue, add a test that parses a workflow containing a non-
core_metadatastateless input with a value, then builds the graph and asserts that value is not serialized unless the node type explicitly accepts extras. -
invokeai/frontend/web/src/features/nodes/util/workflow/validateWorkflow.ts:102The PR exempts
core_metadataextras ingraphToWorkflow,fieldValidators,InputFieldGate, andbuildNodesGraph, but not invalidateWorkflow.validateWorkflowstill warns whenever an input lacks a template. That means the very extras this PR preserves, such asz_image_seed_variance_enabled, become workflow-load warnings.Loading from image uses
graphToWorkflowwhen an image has a graph but no workflow atinvokeai/frontend/web/src/features/workflowLibrary/hooks/useLoadWorkflowFromImage.ts:35. It then callsvalidateWorkflowthroughinvokeai/frontend/web/src/features/workflowLibrary/hooks/useValidateAndLoadWorkflow.ts:54. For each synthesizedcore_metadataextra,template.inputs[input.name]is missing, so validation pushesnodes.missingFieldTemplateatinvokeai/frontend/web/src/features/nodes/util/workflow/validateWorkflow.ts:103. The caller then shows "loaded with warnings" atinvokeai/frontend/web/src/features/workflowLibrary/hooks/useValidateAndLoadWorkflow.ts:88.Recall/load an image whose saved graph contains
core_metadataextras. The workflow loads, but the user gets a warning for intentionally valid metadata fields.To expose this issue, add a
validateWorkflowtest using acore_metadataworkflow node with an undeclared extra and assert validation returns no warnings while preserving the input. -
invokeai/frontend/web/src/features/nodes/util/node/nodeUpdate.ts:61:updateNodestill trims node inputs tokeys(defaults.data.inputs). Ifcore_metadataever gets a compatible template version bump, update can drop the preserved extra inputs. This issue depends on a future version mismatch, but it is the same preservation gap in another workflow path. -
invokeai/frontend/web/src/features/nodes/util/workflow/graphToWorkflow.test.ts: the tests use proxy templates whoseinputsappear to contain any requested key. This is useful for graph breadth, but it hides real missing-template behavior. A direct test with ordinary object templates would better cover the synthesized-extra path. -
The new metadata value schemas intentionally model metadata as opaque object arrays or arbitrary values. That matches the round-trip goal, but it makes the parser less capable of catching malformed metadata. The key safety boundary should be scoping: arbitrary extras should be accepted only after the node type is known to accept them.
…nodes The MetadataExtraField input instance (value: z.any()) was added to the global field-instance union, so it matched any malformed input value on any node during workflow parsing (inputs are parsed without their template). Stale connection-only values were preserved instead of coerced to undefined and could leak into the backend graph via buildNodesGraph. Scope extras to node types that accept them (pydantic extra='allow', e.g. core_metadata): - Remove MetadataExtraField from the global stateful input-instance and value unions; add a dedicated zFieldInputInstanceWithExtras union - Parse node inputs in zInvocationNodeData based on node type: extra- accepting nodes use the with-extras union, all others the strict union - Removing z.any() from zStatefulFieldValue also restores proper typing for StatefulFieldValue/FieldValue/FieldInputInstance (were collapsing to any) - validateWorkflow: don't warn about undeclared extras on extra-accepting nodes (fixes spurious "loaded with warnings" on image recall) - nodeUpdate: preserve extras across template version bumps Add tests for the scoping boundary and the validateWorkflow behavior.
JPPhoto
left a comment
There was a problem hiding this comment.
The prior findings were addressed for the MetadataExtraField catch-all and workflow-load warnings. It looks like there's one remaining issue, a regression:
-
invokeai/frontend/web/src/features/nodes/types/field.ts:1271The updated PR scopes
MetadataExtraFieldcorrectly, but the new concrete metadata field instances are still global.zMetadataPassthroughValueaccepts anyarray(record(string, any))or nullish value, and the four metadata input schemas using it are included in the normalzFieldInputInstanceunion atinvokeai/frontend/web/src/features/nodes/types/field.ts:1450.zInvocationNodeDatauses that normal union for every non-extra node atinvokeai/frontend/web/src/features/nodes/types/invocation.ts:50, before the parser has access to field templates.This is a regression; inputs are parsed without their template type. So a non-
core_metadataworkflow input with a stale array-of-object value can now parse asLoRAMetadataFieldInputInstanceor another metadata passthrough instance even if the actual template is a stateless connection-only field. ThenbuildNodesGraphserializes values for all known template fields atinvokeai/frontend/web/src/features/nodes/util/graph/buildNodesGraph.ts:75. For example,img_resize.metadatais aMetadataFieldconnection-only input ininvokeai/frontend/web/src/features/nodes/store/util/testUtils.ts:346, but a saved value like[{foo: "bar"}]would now survive parsing and be sent as a direct graph input.To trigger this, load a stale or malformed workflow where a non-extra node has a connection-only/stateless input containing an array of objects. Before this PR, that value was coerced away by the stateless branch. After this PR, it can be preserved by the new metadata passthrough branch and emitted into the backend graph.
To expose this issue, add a test that parses a non-
core_metadatainvocation node with a connection-onlyMetadataFieldorUNetFieldinput whose value is an array of records, then asserts the parsed value isundefinedandbuildNodesGraphdoes not serialize it. A robust fix would scope the metadata passthrough schemas by node type and/or known field name/template, similar to the newMetadataExtraFieldscoping.
…ng nodes Follow-up to the MetadataExtraField scoping. The concrete metadata pass-through instances (LoRA/ControlNet/IPAdapter/T2IAdapter MetadataField) use zMetadataPassthroughValue (array(record(string, any)) | nullish), which is greedy enough to match a stale array-of-objects value on any field. They were still in the global zStatefulFieldInputInstance union, which zInvocationNodeData uses for every non-extra node - and inputs are parsed without their template. So a stale value on a connection-only input (e.g. img_resize.metadata) could survive parsing as a metadata instance and leak into the backend graph via buildNodesGraph. Move the four metadata pass-through instances out of the global union into the scoped zFieldInputInstanceWithExtras union (used only for nodes that accept extras, i.e. core_metadata). The FieldInputInstance type is now derived from a dedicated union that includes the metadata shapes (so builders type-check) but excludes the MetadataExtraField z.any() catch-all. Add a buildNodesGraph regression test (stale array-of-records on a connection-only input is coerced away and not serialized) plus parse-level coverage; update loraMetadataRoundtrip tests to use the scoped union.
|
Looks good, approved! |
…ta-loras-roundtrip # Conflicts: # invokeai/frontend/web/src/features/nodes/types/invocation.ts # invokeai/frontend/web/src/features/nodes/util/graph/buildNodesGraph.test.ts # invokeai/frontend/web/src/features/nodes/util/schema/buildFieldInputTemplate.ts # invokeai/frontend/web/src/features/nodes/util/workflow/graphToWorkflow.test.ts # invokeai/frontend/web/src/features/nodes/util/workflow/validateWorkflow.test.ts
Summary
The
core_metadatanode is configured with pydanticextra='allow'and carries recall metadata (loras,controlnets,ipAdapters,t2iAdapters, plus model-specific extras likez_image_seed_variance_*,dype_preset,ref_images). When a generated image's graph was loaded into the workflow editor and saved, those values were being dropped:*MetadataFieldcollection types were not registered asStatefulFieldType, so their inputs fell through tozStatelessFieldInputInstance, whosevalueisz.undefined().catch(undefined)— silently coercing the array away.graphToWorkflow, becausetemplate.inputs[key]was undefined and the field was skipped with a warning.Result: the regenerated image had no recall metadata and "Recall all parameters" reported no LoRA / no variance settings — even though the backend executed the workflow correctly (
lora_selectorand thez_image_seed_variance_enhancernode were intact via their edges).This change:
LoRAMetadataField,ControlNetMetadataField,IPAdapterMetadataField,T2IAdapterMetadataFieldas stateful field types with passthrough zod values.MetadataExtraFieldso undeclared keys onextra='allow'nodes round-trip through the workflow editor.graphToWorkflowsynthesizes an extra-field template for keys not in the node template, scoped to nodes that accept extras.buildNodesGraphforwards extra values verbatim when running the workflow.fieldValidatorsandInputFieldGateno longer treat undeclared inputs on extra-accepting nodes as errors / unexpected fields.Adds regression tests covering the LoRA roundtrip, the extras roundtrip, and the
parseSchematemplate type forcore_metadata.loras.Related Issues / Discussions
Fixes #9151
QA Instructions
Tested end-to-end against a real Invoke instance (v6.12.0, Z-Image Turbo) with a real LoRA. Reproduction steps for reviewers:
strength > 0).lorasarray is present, with the same model and weight as the original.z_image_seed_variance_enabled,z_image_seed_variance_strength,z_image_seed_variance_randomize_percentare present.Automated coverage (added in this PR):
loraMetadataRoundtrip.test.ts—zFieldInputInstancepreserves values for all four*MetadataFieldtypes and acceptsnull.graphToWorkflow.test.ts— full graph → workflow →parseAndMigrateWorkflowroundtrip preserves bothcore_metadata.lorasand undeclared extras (z_image_seed_variance_*).parseSchemaCoreMetadata.test.ts—parseSchemaproduces a statefulLoRAMetadataFieldtemplate forcore_metadata.loras(not a stateless fallback).Run with:
cd invokeai/frontend/web pnpm test:no-watchMerge Plan
Frontend-only change, no migrations and no backend schema impact. Safe to merge whenever; consider including in a patch release alongside other Z-Image bug fixes since the user-visible symptom (LoRA appears to be ignored on workflow re-runs) is a regression hot spot.
Checklist
What's Newcopy (if doing a release after this PR) — defer to release author