@@ -192,6 +192,156 @@ def test_custom_block_supported_components(self):
192192 assert len (pipe .components ) == 1
193193 assert pipe .component_names [0 ] == "transformer"
194194
195+ def test_trust_remote_code_not_propagated_to_external_repo (self ):
196+ """When a modular pipeline repo references a component from an external repo that has custom
197+ code (auto_map in config), calling load_components(trust_remote_code=True) should NOT
198+ propagate trust_remote_code to that external component. The external component should fail
199+ to load."""
200+
201+ from diffusers import ModularPipeline
202+
203+ CUSTOM_MODEL_CODE = (
204+ "import torch\n "
205+ "from diffusers import ModelMixin, ConfigMixin\n "
206+ "from diffusers.configuration_utils import register_to_config\n "
207+ "\n "
208+ "class CustomModel(ModelMixin, ConfigMixin):\n "
209+ " @register_to_config\n "
210+ " def __init__(self, hidden_size=8):\n "
211+ " super().__init__()\n "
212+ " self.linear = torch.nn.Linear(hidden_size, hidden_size)\n "
213+ "\n "
214+ " def forward(self, x):\n "
215+ " return self.linear(x)\n "
216+ )
217+
218+ with tempfile .TemporaryDirectory () as external_repo_dir , tempfile .TemporaryDirectory () as pipeline_repo_dir :
219+ # Step 1: Create an external model repo with custom code (requires trust_remote_code)
220+ with open (os .path .join (external_repo_dir , "modeling.py" ), "w" ) as f :
221+ f .write (CUSTOM_MODEL_CODE )
222+
223+ config = {
224+ "_class_name" : "CustomModel" ,
225+ "_diffusers_version" : "0.0.0" ,
226+ "auto_map" : {"AutoModel" : "modeling.CustomModel" },
227+ "hidden_size" : 8 ,
228+ }
229+ with open (os .path .join (external_repo_dir , "config.json" ), "w" ) as f :
230+ json .dump (config , f )
231+
232+ torch .save ({}, os .path .join (external_repo_dir , "diffusion_pytorch_model.bin" ))
233+
234+ # Step 2: Create a custom block that references the external repo.
235+ # Define both the class (for direct use) and its code string (for block.py).
236+ class ExternalRefBlock (ModularPipelineBlocks ):
237+ @property
238+ def expected_components (self ):
239+ return [
240+ ComponentSpec (
241+ "custom_model" ,
242+ AutoModel ,
243+ pretrained_model_name_or_path = external_repo_dir ,
244+ )
245+ ]
246+
247+ @property
248+ def inputs (self ) -> List [InputParam ]:
249+ return [InputParam ("prompt" , type_hint = str , required = True )]
250+
251+ @property
252+ def intermediate_inputs (self ) -> List [InputParam ]:
253+ return []
254+
255+ @property
256+ def intermediate_outputs (self ) -> List [OutputParam ]:
257+ return [OutputParam ("output" , type_hint = str )]
258+
259+ def __call__ (self , components , state : PipelineState ) -> PipelineState :
260+ block_state = self .get_block_state (state )
261+ block_state .output = "test"
262+ self .set_block_state (state , block_state )
263+ return components , state
264+
265+ EXTERNAL_REF_BLOCK_CODE_STR = (
266+ "from typing import List\n "
267+ "from diffusers import AutoModel\n "
268+ "from diffusers.modular_pipelines import (\n "
269+ " ComponentSpec,\n "
270+ " InputParam,\n "
271+ " ModularPipelineBlocks,\n "
272+ " OutputParam,\n "
273+ " PipelineState,\n "
274+ ")\n "
275+ "\n "
276+ "class ExternalRefBlock(ModularPipelineBlocks):\n "
277+ " @property\n "
278+ " def expected_components(self):\n "
279+ " return [\n "
280+ " ComponentSpec(\n "
281+ ' "custom_model",\n '
282+ " AutoModel,\n "
283+ f' pretrained_model_name_or_path="{ external_repo_dir } ",\n '
284+ " )\n "
285+ " ]\n "
286+ "\n "
287+ " @property\n "
288+ " def inputs(self) -> List[InputParam]:\n "
289+ ' return [InputParam("prompt", type_hint=str, required=True)]\n '
290+ "\n "
291+ " @property\n "
292+ " def intermediate_inputs(self) -> List[InputParam]:\n "
293+ " return []\n "
294+ "\n "
295+ " @property\n "
296+ " def intermediate_outputs(self) -> List[OutputParam]:\n "
297+ ' return [OutputParam("output", type_hint=str)]\n '
298+ "\n "
299+ " def __call__(self, components, state: PipelineState) -> PipelineState:\n "
300+ " block_state = self.get_block_state(state)\n "
301+ ' block_state.output = "test"\n '
302+ " self.set_block_state(state, block_state)\n "
303+ " return components, state\n "
304+ )
305+
306+ # Save the block config, write block.py, then load back via from_pretrained
307+ block = ExternalRefBlock ()
308+ block .save_pretrained (pipeline_repo_dir )
309+
310+ # auto_map will reference the module name derived from ExternalRefBlock.__module__,
311+ # which is "test_modular_pipelines_custom_blocks". Write the code file with that name.
312+ code_path = os .path .join (pipeline_repo_dir , "test_modular_pipelines_custom_blocks.py" )
313+ with open (code_path , "w" ) as f :
314+ f .write (EXTERNAL_REF_BLOCK_CODE_STR )
315+
316+ block = ModularPipelineBlocks .from_pretrained (pipeline_repo_dir , trust_remote_code = True )
317+ pipe = block .init_pipeline ()
318+ pipe .save_pretrained (pipeline_repo_dir )
319+
320+ # Step 3: Load the pipeline from the saved directory.
321+ loaded_pipe = ModularPipeline .from_pretrained (pipeline_repo_dir , trust_remote_code = True )
322+
323+ assert loaded_pipe ._pretrained_model_name_or_path == pipeline_repo_dir
324+ assert loaded_pipe ._component_specs ["custom_model" ].pretrained_model_name_or_path == external_repo_dir
325+ assert getattr (loaded_pipe , "custom_model" , None ) is None
326+
327+ # Step 4a: load_components WITHOUT trust_remote_code.
328+ # It should still fail
329+ loaded_pipe .load_components ()
330+ assert getattr (loaded_pipe , "custom_model" , None ) is None
331+
332+ # Step 4b: load_components with trust_remote_code=True.
333+ # trust_remote_code should be stripped for the external component, so it fails.
334+ # The warning should contain guidance about manually loading with trust_remote_code.
335+ loaded_pipe .load_components (trust_remote_code = True )
336+ assert getattr (loaded_pipe , "custom_model" , None ) is None
337+
338+ # Step 4c: Manually load with AutoModel and update_components — this should work.
339+ from diffusers import AutoModel
340+
341+ custom_model = AutoModel .from_pretrained (external_repo_dir , trust_remote_code = True )
342+ loaded_pipe .update_components (custom_model = custom_model )
343+ assert getattr (loaded_pipe , "custom_model" , None ) is not None
344+
195345 def test_custom_block_loads_from_hub (self ):
196346 repo_id = "hf-internal-testing/tiny-modular-diffusers-block"
197347 block = ModularPipelineBlocks .from_pretrained (repo_id , trust_remote_code = True )
0 commit comments