feat: add configurable image subfolder strategies#8969
feat: add configurable image subfolder strategies#8969Pfannkuchensack wants to merge 16 commits intoinvoke-ai:mainfrom
Conversation
Add support for organizing output images into subfolders instead of a single flat directory. Four strategies are available via the image_subfolder_strategy config setting: flat (default, current behavior), date (YYYY/MM/DD), type (by image category), and hash (UUID prefix for filesystem performance). The strategy can be changed at any time - existing images keep their paths, only new images use the new strategy.
|
Dropping my comments here because I think this needs some tests:
|
Cover the new subfolder path surface introduced in PR invoke-ai#8969: subfolder validation and security checks, ImageService subfolder forwarding for all strategies, delete_images_on_board silent-failure contract, migration 28, config 4.0.2→4.0.3 upgrade, and recall_parameters subfolder resolution regression.
JPPhoto
left a comment
There was a problem hiding this comment.
One remaining test gap: the PR now covers path validation, service forwarding, recall behavior, config migration, and migration 28, but it still does not verify the SQLite storage contract for image_subfolder.
The risky path is invokeai/app/services/image_records/image_records_sqlite.py, invokeai/app/services/image_records/image_records_sqlite.py, and invokeai/app/services/image_records/image_records_sqlite.py: save(), get()/get_many(), and delete_intermediates() all now depend on real DB round-tripping of image_subfolder. The new tests mostly mock above this layer, so they would not catch a persistence or deserialization regression here. Consider one temporary DB-backed test with the same strategy used in tests/app/services/model_records/test_model_records_sql.py.
More importantly, we collectively still have to have a conversation about how migrating or changing this setting affects existing images, if at all.
…ords_sql.py) to verify image_subfolder round-trips correctly through: TestImageSubfolderRoundTrip (3 tests) — save() -> get() for default empty, custom, and deeply nested subfolders TestGetManySubfolder (1 test) — get_many() deserializes image_subfolder on every row TestDeleteIntermediatesSubfolder (2 tests) — delete_intermediates() returns correct (name, subfolder) pairs and actually removes intermediate rows
Resolve conflict with upstream main: rename PR's migration_28 (image_subfolder) to migration_30 since main now occupies 28 (workflow_library multiuser) and 29 (board_visibility). Update sqlite_util.py registration and rename test_migration_28.py -> test_migration_30.py with corresponding identifier updates.
Summary
Add support for organizing output images into subfolders instead of a single flat directory. Four strategies are available via the
image_subfolder_strategyconfig setting:flat(default): Current behavior, all images in one directorydate: Organized byYYYY/MM/DDtype: Organized by image category (general,intermediate,mask, etc.)hash: UUID prefix for filesystem performance (~256 evenly distributed subfolders)The strategy can be changed at any time — existing images keep their paths, only new images use the new strategy. This is achieved by storing each image's subfolder in a new
image_subfolderDB column.image_name(DB primary key) remains unchanged — no API or frontend changes neededimage_subfoldercolumn stores the relative path prefiximage_subfolder = ""(backward compatible, stays in root)QA Instructions
image_subfolder_strategy: flat) — verify images are saved tooutputs/images/as beforeimage_subfolder_strategy: dateininvokeai.yaml, restart, generate an image — verify it appears inoutputs/images/YYYY/MM/DD/image_subfolder_strategy: type, generate — verify images land inoutputs/images/general/(orintermediate/for intermediate images)image_subfolder_strategy: hash, generate — verify images land inoutputs/images/XX/(first 2 chars of UUID)flat— verify old subfolder images are still accessible in the galleryMerge Plan
4.0.2to4.0.3automaticallyChecklist
What's Newcopy (if doing a release after this PR)