-
Notifications
You must be signed in to change notification settings - Fork 6k
[WIP] Modular Diffusers support custom code/pipeline blocks #11539
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: modular-refactor
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, Dhruv!
I couldn't run the code as there seems to be some problems with undefined variables. I can give the saving logic a look after that is fixed.
@@ -154,15 +157,132 @@ def check_imports(filename): | |||
return get_relative_imports(filename) | |||
|
|||
|
|||
def get_class_in_module(class_name, module_path): | |||
def resolve_trust_remote_code(trust_remote_code, model_name, has_local_code, has_remote_code): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(nit): Would prefer this to be a private method.
f"The repository for {model_name} contains custom code which must be executed to correctly " | ||
f"load the model. You can inspect the repository content at https://hf.co/{model_name}.\n" | ||
f"You can avoid this prompt in future by passing the argument `trust_remote_code=True`.\n\n" | ||
f"Do you wish to run the custom code? [y/N] " | ||
) | ||
if answer.lower() in ["yes", "y", "1"]: | ||
trust_remote_code = True | ||
elif answer.lower() in ["no", "n", "0", ""]: | ||
trust_remote_code = False | ||
signal.alarm(0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it's just about passing trust_remote_code=Yes
, would it be too much to just enforce that and avoid signal
altogether?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could be a case where as a user you don't know it's custom code. This would open a terminal prompt that asks you to confirm whether or not you want to proceed loading the code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No strong opinions but my preference would be to just tell the user how they can resolve that issue with a sensible message instead of opening a terminal to get their choice.
@@ -37,6 +39,7 @@ | |||
|
|||
# See https://huggingface.co/datasets/diffusers/community-pipelines-mirror | |||
COMMUNITY_PIPELINES_MIRROR_ID = "diffusers/community-pipelines-mirror" | |||
_HF_REMOTE_CODE_LOCK = threading.Lock() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we have to acquire a lock?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's used in the step that modifies sys.modules. Assuming it is to avoid having background threads concurrently read/write from there during that operation. It's taken from transformers
custom code loading.
The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update. |
@yiyixuxu PR is ready for an initial review. Lmk if it's fine for this logic to live in ModularPipelineMixin or if it's more appropriate in AutoPipelineBlocks (IMO feels better for it to be here). Apologies for the formatting issues 🙈 I can try to remove them if it's too much. I have not added saving custom modules yet. Think @sayakpaul Wanted to take a look at it. |
Yes, I will take a look into saving. |
if is_accelerate_available(): | ||
import accelerate | ||
pass |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(nit): remove.
trust_remote_code, pretrained_model_name_or_path, has_remote_code | ||
) | ||
if not (has_remote_code and trust_remote_code): | ||
raise ValueError("") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
raise ValueError("") | |
raise ValueError("TODO") |
""" | ||
Import a module on the cache directory for modules and extract a class from it. | ||
""" | ||
module_path = module_path.replace(os.path.sep, ".") | ||
module = importlib.import_module(module_path) | ||
name = os.path.normpath(module_path) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No strong opinions but currently there seems to be a mix of Path
and os
usage. Maybe it's necessary that way but if it's not I would prefer using one of the two.
@@ -454,4 +531,4 @@ def get_class_from_dynamic_module( | |||
revision=revision, | |||
local_files_only=local_files_only, | |||
) | |||
return get_class_in_module(class_name, final_module.replace(".py", "")) | |||
return get_class_in_module(class_name, final_module) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perfect!
super super cool! from diffusers.pipelines.modular_pipeline import ModularPipelineMixin
block = ModularPipelineMixin.from_pretrained(
"YiYiXu/modular-depth-block", trust_remote_code=True
)
print(block)
block.setup_loader()
print(block.loader)
block.loader.load()
url = "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/robot.png"
depth_processed = block.run(url = url, output="image")
print(f"depth_processed.shape: {depth_processed.shape}") |
does that mean only AutoPipelineBlocks will be able to use remote code then? I think all types of blocks should be able to load from remote, no? Maybe we should change Also in Modular system, Block and Pipeline pretty much mean the same thing, there is no clear difference between them. Should we pick up a term and stick to it? which one is better? on a somewhat related note , should I change let me know what you think! |
also, can you think a bit more how would this work with for now, I think if I just change the config_name for |
let's merge this now? we can do follow-up changes in a new PR I'm waiting for this one to get in first so I can merge in all the new changes into the main modular PR and it is a good place for a review (otherwise there might be a lot of merge conflicts for you to resolve) |
Sorry for chiming in
I think the essence is still similar to "automatic selection and configuration". Users are getting the blocks / classes they are expecting to get "automatically". So, I think it's fine. |
What does this PR do?
Add support for loading custom pipeline blocks with Modular Diffusers. PR is still in very rough shape, but is functional.
Snippet to test
Note I think the formatting changes might have been because of a difference in ruff versions.
TODOs:
get_class_from_dynamic_module
. Probably don't need theis_modular
argument.get_class_in_modular_module
trust_remote_code=None
is currently broken. Need to fix.Fixes # (issue)
Before submitting
documentation guidelines, and
here are tips on formatting docstrings.
Who can review?
Anyone in the community is free to review the PR once the tests have passed. Feel free to tag
members/contributors who may be interested in your PR.