feat(architectures): derive the conditioning safe_globals from the registry - #30
Draft
Pfannkuchensack wants to merge 2 commits into
Draft
feat(architectures): derive the conditioning safe_globals from the registry#30Pfannkuchensack wants to merge 2 commits into
Pfannkuchensack wants to merge 2 commits into
Conversation
…gistry ApiDependencies.initialize() passed ObjectSerializerDisk a hand-maintained list of thirteen classes, next to a comment in conditioning_data.py asking whoever adds a conditioning type to remember to update it. Forgetting did not fail at boot: torch.load refuses to deserialize a type that is not on the list, so the error arrived mid-graph, on the first generation that happened to use the new architecture. That is the worst failure mode in the whole inventory, and it is the one this facet removes. No classes move. The original plan proposed relocating the *ConditioningInfo, *ConditioningField and *ConditioningOutput classes into architecture packages; that was dropped. Their names are OpenAPI $refs and appear in stored workflows, so a move is risk without benefit -- and a missing Field or Output class is already an ImportError that tests/test_imports.py catches. Only safe_globals fails late, and registration alone fixes it. Twelve conditioning types serve fifteen architectures: SD1 and SD2 share BasicConditioningInfo, SDXL and its refiner SDXLConditioningInfo, and FLUX.2 Klein reuses FLUXConditioningInfo -- same container, different encoders, with the pooled embedding in clip_embeds and the Qwen3 sequence in t5_embeds. The facet carries a bare `type`. There is no common base or protocol to bind to: SDXLConditioningInfo extends BasicConditioningInfo, but the other ten are unrelated dataclasses. Their one shared property -- membership in ConditioningFieldData.conditionings -- is asserted in CI instead, which is also what keeps that hand-written union and the registry in step. The tests exercise the real mechanism rather than only comparing lists. Each of the twelve types is written through the same ObjectSerializerDisk the app uses and read back, so a type missing from the derived allowlist fails the way it would in production. Verified by omitting one deliberately: torch.load raises UnpicklingError, "Weights only load failed". IPAdapterConditioningInfo stays out, with a test pinning that. It never reaches the serializer -- it lives on IPAdapterData, built and consumed inside a single denoise run. invocation_api now exports all twelve rather than two, with a test tying the export list to the registry. Custom nodes that build conditioning need them. This is the first PR to widen the defs import allowlist, by one entry: conditioning_data. That module is close to a leaf -- its only invokeai import is regional_prompt_data, which imports nothing from invokeai -- so a definition module cannot reach back into the registry through it. That widening has one consequence worth naming: mypy now follows conditioning_data from the architectures package and reports 23 pre-existing errors there, in a module that is not in pyproject's follow_imports overrides. They are unrelated to this change -- checking that file alone reports the same 23 -- so the package gate is now scoped with --follow-imports=silent, which still catches errors in the architectures package itself. Verified by introducing one. openapi.json, schema.ts and docs/src/generated/invocation-context.json are unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This was referenced Aug 2, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fourth of the series from
.ideas/Backend Modularization Plan.md. No behaviour changes.Why this one matters most
ApiDependencies.initialize()passedObjectSerializerDiska hand-maintained list of thirteenclasses, right next to a comment in
conditioning_data.pyasking whoever adds a conditioning type toremember to update it.
Forgetting did not fail at boot.
torch.loadrefuses to deserialize a type that is not on the list,so the error arrived mid-graph, on the first generation that happened to use the new
architecture. That is the worst failure mode in the whole inventory, and it is the one this facet
removes.
Reproduced, by omitting one class deliberately:
No classes move
The original plan proposed relocating the
*ConditioningInfo,*ConditioningFieldand*ConditioningOutputclasses into architecture packages. Dropped: their names are OpenAPI$refsand appear in stored workflows, so a move is risk without benefit — and a missing Field or Output
class is already an
ImportErrorthattests/test_imports.pycatches. Onlysafe_globalsfailslate, and registration alone fixes it.
Twelve types, fifteen architectures
SD1/SD2 share
BasicConditioningInfo, SDXL and its refinerSDXLConditioningInfo, and FLUX.2Klein reuses
FLUXConditioningInfo— same container, different encoders, with the pooled embeddingin
clip_embedsand the Qwen3 sequence int5_embeds.The facet carries a bare
type. There is no common base or protocol to bind to:SDXLConditioningInfoextends
BasicConditioningInfo, but the other ten are unrelated dataclasses. Their one sharedproperty — membership in
ConditioningFieldData.conditionings— is asserted in CI instead, which isalso what keeps that hand-written union and the registry in step.
The tests exercise the mechanism, not just the list
Each of the twelve types is written through the same
ObjectSerializerDiskthe app uses and readback, so a type missing from the derived allowlist fails the way it would in production — rather than
only failing a set comparison that could itself be wrong.
IPAdapterConditioningInfostays out, with a test pinning that: it never reaches the serializer, itlives on
IPAdapterData, built and consumed inside a single denoise run.invocation_apinow exports all twelve rather than two, with a test tying the export list to theregistry. Custom nodes that build conditioning need them.
First widening of the
defsallowlistBy one entry:
conditioning_data. That module is close to a leaf — its onlyinvokeaiimport isregional_prompt_data, which imports nothing frominvokeaiat all — so a definition module cannotreach back into the registry through it. This is exactly the visible, reviewable line the narrow
allowlist in #27 was designed to force.
One consequence worth naming. mypy now follows
conditioning_datafrom the architectures packageand reports 23 pre-existing errors there, in a module that is not in
pyproject'sfollow_importsoverrides. They are unrelated to this change — checking that file alone reports thesame 23 — so the package gate is now scoped with
--follow-imports=silent, which still catcheserrors in the architectures package itself. Verified by introducing one, then reverting it.
Verification
pytest tests/backend/architectures tests/app/util tests/test_imports.py tests/test_docs_json_export.py— 268 passed
mypy --follow-imports=silent invokeai/backend/architectures— cleanruff@0.11.2 check .+format --check— cleanopenapi.jsonregenerated and compared normalized — identical.docs/src/generated/invocation-context.jsonrebuilt viabuild_docs_bundle()— identical.The local model database covers ten of the fifteen architectures (SD1, SDXL, FLUX, FLUX.2, Z-Image,
Krea-2, Qwen-Image, Wan, Anima, Ideogram-4), so a manual generation on any of those exercises the
derived allowlist end to end. SD2, SDXL-Refiner, SD3, CogView4 and ERNIE-Image have no local mains —
for those the round-trip test is the coverage.
🤖 Generated with Claude Code