shap_e model/pipeline review
Commit tested: 0f1abc4ae8b0eb2a3b40e82a310507281144c423
Review performed against the repository review rules.
Duplicate search status: checked GitHub Issues/PRs for shap_e, ShapE, ShapEImg2ImgPipeline, StratifiedRaySampler, mesh output, latent dtype, return_dict, and frame-size/ray batching. I found no likely open duplicates. Closed issue #4075 is only a docstring typo, closed issue #4808 is an old integration snapshot failure, and merged PR #4062 is relevant mesh precedent but does not cover the current gaps.
Issue 1: ShapEImg2ImgPipeline drops or rejects documented image batch inputs
Affected code:
| ifisinstance(image, list) andisinstance(image[0], torch.Tensor): |
| image=torch.cat(image, axis=0) ifimage[0].ndim==4elsetorch.stack(image, axis=0) |
| |
| ifnotisinstance(image, torch.Tensor): |
| image=self.image_processor(image, return_tensors="pt").pixel_values[0].unsqueeze(0) |
| ifisinstance(image, PIL.Image.Image): |
| batch_size=1 |
| elifisinstance(image, torch.Tensor): |
| batch_size=image.shape[0] |
| elifisinstance(image, list) andisinstance(image[0], (torch.Tensor, PIL.Image.Image)): |
| batch_size=len(image) |
| else: |
| raiseValueError( |
| f"`image` has to be of type `PIL.Image.Image`, `torch.Tensor`, `list[PIL.Image.Image]` or `list[torch.Tensor]` but is {type(image)}" |
| ) |
Problem:
The docstring says image accepts np.ndarray and list[np.ndarray], but __call__ rejects those types. For list[PIL.Image.Image], _encode_image() calls the image processor and then indexes [0].unsqueeze(0), silently keeping only the first processed image.
Impact:
Documented batched image inputs fail with shape errors or are reduced to one image. This also leaves fast tests blind to PIL/NumPy image batches because they use tensor inputs.
Reproduction:
fromPILimportImageimportnumpyasnpimporttorchfromdiffusersimportShapEImg2ImgPipelinefromtransformersimportCLIPImageProcessor, CLIPVisionConfig, CLIPVisionModelimage_encoder=CLIPVisionModel(CLIPVisionConfig(hidden_size=8, image_size=32, intermediate_size=16, num_attention_heads=2, num_hidden_layers=1, patch_size=1))
image_processor=CLIPImageProcessor(do_resize=True, size={"shortest_edge": 32}, do_center_crop=True, crop_size={"height": 32, "width": 32}, do_normalize=False)
pipe=ShapEImg2ImgPipeline(None, image_encoder, image_processor, None, None)
images= [Image.fromarray(np.zeros((32, 32, 3), dtype=np.uint8)) for_inrange(2)]
embeds=pipe._encode_image(images, torch.device("cpu"), 1, False)
print(embeds.shape[0]) # 1, expected 2Relevant precedent:
Stable Diffusion img2img preprocesses the whole input batch instead of indexing the first item:
| image=self.image_processor.preprocess(image) |
Suggested fix:
elifisinstance(image, np.ndarray):
batch_size=1ifimage.ndim==3elseimage.shape[0]
elifisinstance(image, list) andisinstance(image[0], (torch.Tensor, PIL.Image.Image, np.ndarray)):
batch_size=len(image)
...
ifnotisinstance(image, torch.Tensor):
image=self.image_processor(image, return_tensors="pt").pixel_values
Issue 2: Provided latents are not consistently validated or cast to model dtype
Affected code:
| defprepare_latents(self, shape, dtype, device, generator, latents, scheduler): |
| iflatentsisNone: |
| latents=randn_tensor(shape, generator=generator, device=device, dtype=dtype) |
| else: |
| iflatents.shape!=shape: |
| raiseValueError(f"Unexpected latents shape, got {latents.shape}, expected {shape}") |
| latents=latents.to(device) |
| |
| latents=latents*scheduler.init_noise_sigma |
| # Copied from diffusers.pipelines.deprecated.unclip.pipeline_unclip.UnCLIPPipeline.prepare_latents |
| defprepare_latents(self, shape, dtype, device, generator, latents, scheduler): |
| iflatentsisNone: |
| latents=randn_tensor(shape, generator=generator, device=device, dtype=dtype) |
| else: |
| iflatents.shape!=shape: |
| raiseValueError(f"Unexpected latents shape, got {latents.shape}, expected {shape}") |
| latents=latents.to(device) |
| |
| latents=latents*scheduler.init_noise_sigma |
| returnlatents |
| iflatentsisNone: |
| latents=self.prepare_latents( |
| (batch_size, num_embeddings*embedding_dim), |
| image_embeds.dtype, |
| device, |
| generator, |
| latents, |
| self.scheduler, |
| ) |
| |
| # YiYi notes: for testing only to match ldm, we can directly create a latents with desired shape: batch_size, num_embeddings, embedding_dim |
| latents=latents.reshape(latents.shape[0], num_embeddings, embedding_dim) |
Problem:
prepare_latents() moves user-provided latents to the device but not the requested dtype. In img2img, provided latents skip prepare_latents() entirely, so they also skip shape validation and scheduler scaling.
Impact:
Half-precision pipelines fail with dtype mismatch when users pass normal float32 latents. Img2img can fail later inside PriorTransformer with cryptic batch/shape errors instead of raising at input validation.
Reproduction:
importtypesimporttorchfromdiffusersimportHeunDiscreteScheduler, PriorTransformer, ShapEPipelineprior=PriorTransformer(
num_attention_heads=2, attention_head_dim=8, embedding_dim=8, num_embeddings=4,
embedding_proj_dim=16, time_embed_dim=32, num_layers=1, clip_embed_dim=16,
additional_embeddings=0, norm_in_type="layer", encoder_hid_proj_type=None, added_emb_type=None,
).to(dtype=torch.float16)
scheduler=HeunDiscreteScheduler(beta_schedule="exp", num_train_timesteps=8, prediction_type="sample")
pipe=ShapEPipeline(prior, None, None, scheduler, None)
pipe.set_progress_bar_config(disable=True)
pipe._encode_prompt=types.MethodType(lambdaself, *args, **kwargs: torch.zeros(1, 16, dtype=torch.float16), pipe)
latents=torch.zeros(1, 4*8, dtype=torch.float32)
pipe("x", latents=latents, num_inference_steps=1, guidance_scale=1.0, output_type="latent")Relevant precedent:
Flux2 casts provided latents to both device and dtype:
| latents=latents.to(device=device, dtype=dtype) |
Suggested fix:
latents=latents.to(device=device, dtype=dtype)
For img2img, call prepare_latents(...) unconditionally, as the text pipeline does, so provided latents get the same validation/scaling path.
Issue 3: Fresh ShapERenderer mesh output has an empty marching-cubes lookup table
Affected code:
| def__init__(self): |
| super().__init__() |
| cases=torch.zeros(256, 5, 3, dtype=torch.long) |
| masks=torch.zeros(256, 5, dtype=torch.bool) |
| |
| self.register_buffer("cases", cases) |
| self.register_buffer("masks", masks) |
| You can generate mesh outputs for both the [`ShapEPipeline`] and [`ShapEImg2ImgPipeline`] by specifying the `output_type` parameter as `"mesh"`: |
| |
| ```py |
| import torch |
| from diffusers import ShapEPipeline |
| |
| device = torch.device("cuda"if torch.cuda.is_available() else"cpu") |
| |
| pipe = ShapEPipeline.from_pretrained("openai/shap-e", torch_dtype=torch.float16, variant="fp16") |
| pipe = pipe.to(device) |
| |
| guidance_scale =15.0 |
| prompt ="A birthday cupcake" |
| |
| images = pipe(prompt, guidance_scale=guidance_scale, num_inference_steps=64, frame_size=256, output_type="mesh").images |
| ``` |
Problem:
MeshDecoder initializes cases and masks to zeros. The conversion script populates those buffers, but the runtime constructor does not. A renderer created from config or in fast tests cannot produce mesh geometry.
Impact:
output_type="mesh" is documented, but fresh/local renderer construction produces empty meshes and can later fail with empty texture batches. Tests do not catch this path.
Reproduction:
importtorchfromdiffusers.pipelines.shap_e.rendererimportMeshDecoderdecoder=MeshDecoder()
print(int(decoder.cases.abs().sum()), int(decoder.masks.sum())) # 0 0field=torch.randn(4, 4, 4)
mesh=decoder(field, torch.tensor([-1., -1., -1.]), torch.tensor([2., 2., 2.]))
print(mesh.verts.shape, mesh.faces.shape) # empty geometry
Relevant precedent:
The conversion script already has the lookup-table builder:
| defcreate_mc_lookup_table(): |
| cases=torch.zeros(256, 5, 3, dtype=torch.long) |
| masks=torch.zeros(256, 5, dtype=torch.bool) |
| |
| edge_to_index= { |
| (0, 1): 0, |
| (2, 3): 1, |
| (4, 5): 2, |
| (6, 7): 3, |
| (0, 2): 4, |
| (1, 3): 5, |
| (4, 6): 6, |
| (5, 7): 7, |
| (0, 4): 8, |
| (1, 5): 9, |
| (2, 6): 10, |
| (3, 7): 11, |
| } |
| |
| fori, caseinenumerate(MC_TABLE): |
| forj, triinenumerate(case): |
| fork, (c1, c2) inenumerate(zip(tri[::2], tri[1::2])): |
| cases[i, j, k] =edge_to_index[(c1, c2) ifc1<c2else (c2, c1)] |
| masks[i, j] =True |
| returncases, masks |
| |
| |
| RENDERER_CONFIG= {} |
| |
| |
| defrenderer_model_from_original_config(): |
| model=ShapERenderer(**RENDERER_CONFIG) |
| |
| returnmodel |
| |
| |
| RENDERER_MLP_ORIGINAL_PREFIX="renderer.nerstf" |
| |
| RENDERER_PARAMS_PROJ_ORIGINAL_PREFIX="encoder.params_proj" |
| |
| |
| defrenderer_model_original_checkpoint_to_diffusers_checkpoint(model, checkpoint): |
| diffusers_checkpoint= {} |
| diffusers_checkpoint.update( |
| {f"mlp.{k}": checkpoint[f"{RENDERER_MLP_ORIGINAL_PREFIX}.{k}"] forkinmodel.mlp.state_dict().keys()} |
| ) |
| |
| diffusers_checkpoint.update( |
| { |
| f"params_proj.{k}": checkpoint[f"{RENDERER_PARAMS_PROJ_ORIGINAL_PREFIX}.{k}"] |
| forkinmodel.params_proj.state_dict().keys() |
| } |
| ) |
| |
| diffusers_checkpoint.update({"void.background": model.state_dict()["void.background"]}) |
| |
| cases, masks=create_mc_lookup_table() |
| |
| diffusers_checkpoint.update({"mesh_decoder.cases": cases}) |
| diffusers_checkpoint.update({"mesh_decoder.masks": masks}) |
Suggested fix:
Move the marching-cubes table generation into runtime code, or store a static table used by MeshDecoder.__init__(). Keep loading checkpoint buffers for backwards compatibility, but the default constructor should initialize functional cases and masks.
Issue 4: decode_to_image() drops remainder rays and fails for many frame_size values
Affected code:
| # create cameras object |
| camera=create_pan_cameras(size) |
| rays=camera.camera_rays |
| rays=rays.to(device) |
| n_batches=rays.shape[1] //ray_batch_size |
| |
| coarse_sampler=StratifiedRaySampler() |
| |
| images= [] |
| |
| foridxinrange(n_batches): |
| rays_batch=rays[:, idx*ray_batch_size : (idx+1) *ray_batch_size] |
| |
| # render rays with coarse, stratified samples. |
| _, fine_sampler, coarse_model_out=self.render_rays(rays_batch, coarse_sampler, n_coarse_samples) |
| # Then, render with additional importance-weighted ray samples. |
| channels, _, _=self.render_rays( |
| rays_batch, fine_sampler, n_fine_samples, prev_model_out=coarse_model_out |
| ) |
| |
| images.append(channels) |
| |
| images=torch.cat(images, dim=1) |
| images=images.view(*camera.shape, camera.height, camera.width, -1).squeeze(0) |
Problem:
n_batches = rays.shape[1] // ray_batch_size uses floor division. If total rays are smaller than ray_batch_size, images remains empty. If total rays are not exactly divisible, the tail rays are dropped and the final .view(...) shape is invalid.
Impact:
Users can pass arbitrary frame_size, but many sizes fail at render time. Current tests only use latent output in fast tests and frame_size=64 in nightly tests, so this is uncovered.
Reproduction:
importtorchfromdiffusers.pipelines.shap_eimportShapERendererrenderer=ShapERenderer(
param_shapes=((8, 93), (8, 8), (8, 8), (8, 8)),
d_latent=16,
d_hidden=8,
n_output=12,
)
latents=torch.zeros(1, 32, 16)
renderer.decode_to_image(latents, torch.device("cpu"), size=9)Relevant precedent:
No duplicate found.
Suggested fix:
forstartinrange(0, rays.shape[1], ray_batch_size):
rays_batch=rays[:, start : start+ray_batch_size]
_, fine_sampler, coarse_model_out=self.render_rays(rays_batch, coarse_sampler, n_coarse_samples)
channels, _, _=self.render_rays(rays_batch, fine_sampler, n_fine_samples, prev_model_out=coarse_model_out)
images.append(channels)
Issue 5: Renderer sampling resets the global PyTorch RNG
Affected code:
| mids=0.5* (ts[..., 1:] +ts[..., :-1]) |
| upper=torch.cat([mids, t1], dim=-1) |
| lower=torch.cat([t0, mids], dim=-1) |
| # yiyi notes: add a random seed here for testing, don't forget to remove |
| torch.manual_seed(0) |
| t_rand=torch.rand_like(ts) |
| |
| ts=lower+ (upper-lower) *t_rand |
| t_rand=torch.rand(inds.shape, device=inds.device) |
| lower_=torch.gather(lower, -2, inds) |
| upper_=torch.gather(upper, -2, inds) |
| |
| ts=lower_+ (upper_-lower_) *t_rand |
Problem:
StratifiedRaySampler.sample() calls torch.manual_seed(0) inside production rendering. This mutates global RNG state, and the adjacent comment is explicitly temporary/debug context.
Impact:
Calling Shap-E rendering changes the caller’s global RNG sequence. It can also make each ray batch reuse the same sampling pattern.
Reproduction:
importtorchfromdiffusers.pipelines.shap_e.rendererimportStratifiedRaySamplertorch.manual_seed(123)
sampler=StratifiedRaySampler()
sampler.sample(torch.zeros(1, 1), torch.ones(1, 1), 4)
print(torch.initial_seed()) # 0, expected to remain 123
Relevant precedent:
No duplicate found.
Suggested fix:
Thread a local torch.Generator through decode_to_image(), render_rays(), StratifiedRaySampler.sample(), ImportanceRaySampler.sample(), and sample_pmf(). If snapshot stability is required, seed that local generator with 0 without touching global state.
Issue 6: return_dict=False is ignored for latent outputs
Affected code:
| ifoutput_type=="latent": |
| returnShapEPipelineOutput(images=latents) |
| ifoutput_type=="latent": |
| returnShapEPipelineOutput(images=latents) |
Problem:
Both pipelines return ShapEPipelineOutput immediately for output_type="latent", before the shared if not return_dict branch.
Impact:
The public return_dict=False contract changes based on output type.
Reproduction:
importtypesimporttorchfromdiffusersimportHeunDiscreteScheduler, PriorTransformer, ShapEPipelineprior=PriorTransformer(num_attention_heads=2, attention_head_dim=8, embedding_dim=8, num_embeddings=4, embedding_proj_dim=16, time_embed_dim=32, num_layers=1, clip_embed_dim=16, additional_embeddings=0, norm_in_type="layer", encoder_hid_proj_type=None, added_emb_type=None)
scheduler=HeunDiscreteScheduler(beta_schedule="exp", num_train_timesteps=8, prediction_type="sample")
pipe=ShapEPipeline(prior, None, None, scheduler, None)
pipe.set_progress_bar_config(disable=True)
pipe._encode_prompt=types.MethodType(lambdaself, *args, **kwargs: torch.zeros(1, 16), pipe)
out=pipe("x", num_inference_steps=1, guidance_scale=1.0, output_type="latent", return_dict=False)
print(type(out).__name__, isinstance(out, tuple)) # ShapEPipelineOutput FalseRelevant precedent:
Flux keeps latent handling before the final return_dict branch:
| ifoutput_type=="latent": |
| image=latents |
| else: |
| latents=self._unpack_latents(latents, height, width, self.vae_scale_factor) |
| latents= (latents/self.vae.config.scaling_factor) +self.vae.config.shift_factor |
| image=self.vae.decode(latents, return_dict=False)[0] |
| image=self.image_processor.postprocess(image, output_type=output_type) |
| |
| # Offload all models |
| self.maybe_free_model_hooks() |
| |
| ifnotreturn_dict: |
| return (image,) |
| |
| returnFluxPipelineOutput(images=image) |
Suggested fix:
ifoutput_type=="latent":
images=latentselse:
...
ifnotreturn_dict:
return (images,)
returnShapEPipelineOutput(images=images)
Issue 7: DifferentiableProjectiveCamera.resize_image() cannot construct the resized camera
Affected code:
| defresize_image(self, width: int, height: int) ->"DifferentiableProjectiveCamera": |
| """ |
| Creates a new camera for the resized view assuming the aspect ratio does not change. |
| """ |
| assertwidth*self.height==height*self.width, "The aspect ratio should not change." |
| returnDifferentiableProjectiveCamera( |
| origin=self.origin, |
| x=self.x, |
| y=self.y, |
| z=self.z, |
| width=width, |
| height=height, |
| x_fov=self.x_fov, |
| y_fov=self.y_fov, |
| ) |
Problem:
DifferentiableProjectiveCamera requires shape, but resize_image() omits it when constructing the replacement camera.
Impact:
The camera helper returned by create_pan_cameras() exposes a broken resize method.
Reproduction:
fromdiffusers.pipelines.shap_eimportcreate_pan_camerascam=create_pan_cameras(32)
cam.resize_image(64, 64)
Relevant precedent:
No duplicate found.
Suggested fix:
returnDifferentiableProjectiveCamera(
origin=self.origin,
x=self.x,
y=self.y,
z=self.z,
width=width,
height=height,
x_fov=self.x_fov,
y_fov=self.y_fov,
shape=self.shape,
)
Issue 8: Slow tests are missing, and offload coverage is skipped
Affected code:
| @unittest.skip("Key error is raised with accelerate") |
| deftest_sequential_cpu_offload_forward_pass(self): |
| pass |
| |
| |
| @nightly |
| @require_torch_accelerator |
| @unittest.skip("Key error is raised with accelerate") |
| deftest_sequential_cpu_offload_forward_pass(self): |
| pass |
| |
| |
| @nightly |
| @require_torch_accelerator |
Problem:
The target has fast tests and nightly integration tests, but no @slow tests. Both sequential CPU offload tests are explicitly skipped with “Key error is raised with accelerate.”
Impact:
RUN_SLOW does not exercise Shap-E, mesh output is untested, non-latent rendering is only covered nightly, and offload behavior is known-uncovered despite being in scope for pipeline review.
Reproduction:
frompathlibimportPathforpathinsorted(Path("tests/pipelines/shap_e").glob("test_*.py")):
text=path.read_text()
print(path, "@slow"intext, "@nightly"intext)Relevant precedent:
No duplicate found.
Suggested fix:
Add slow imports and @slow coverage for at least one pretrained text-to-3D and img2img run, plus a small output_type="mesh" assertion. Unskip or replace the sequential CPU offload tests with a current accelerate-compatible offload regression test.
shap_emodel/pipeline reviewCommit tested:
0f1abc4ae8b0eb2a3b40e82a310507281144c423Review performed against the repository review rules.
Duplicate search status: checked GitHub Issues/PRs for
shap_e,ShapE,ShapEImg2ImgPipeline,StratifiedRaySampler, mesh output, latent dtype,return_dict, and frame-size/ray batching. I found no likely open duplicates. Closed issue #4075 is only a docstring typo, closed issue #4808 is an old integration snapshot failure, and merged PR #4062 is relevant mesh precedent but does not cover the current gaps.Issue 1:
ShapEImg2ImgPipelinedrops or rejects documented image batch inputsAffected code:
diffusers/src/diffusers/pipelines/shap_e/pipeline_shap_e_img2img.py
Lines 149 to 153 in 0f1abc4
diffusers/src/diffusers/pipelines/shap_e/pipeline_shap_e_img2img.py
Lines 225 to 234 in 0f1abc4
Problem:
The docstring says
imageacceptsnp.ndarrayandlist[np.ndarray], but__call__rejects those types. Forlist[PIL.Image.Image],_encode_image()calls the image processor and then indexes[0].unsqueeze(0), silently keeping only the first processed image.Impact:
Documented batched image inputs fail with shape errors or are reduced to one image. This also leaves fast tests blind to PIL/NumPy image batches because they use tensor inputs.
Reproduction:
Relevant precedent:
Stable Diffusion img2img preprocesses the whole input batch instead of indexing the first item:
diffusers/src/diffusers/pipelines/stable_diffusion/pipeline_stable_diffusion_img2img.py
Line 1048 in 0f1abc4
Suggested fix:
Issue 2: Provided latents are not consistently validated or cast to model dtype
Affected code:
diffusers/src/diffusers/pipelines/shap_e/pipeline_shap_e.py
Lines 130 to 138 in 0f1abc4
diffusers/src/diffusers/pipelines/shap_e/pipeline_shap_e_img2img.py
Lines 130 to 140 in 0f1abc4
diffusers/src/diffusers/pipelines/shap_e/pipeline_shap_e_img2img.py
Lines 250 to 261 in 0f1abc4
Problem:
prepare_latents()moves user-provided latents to the device but not the requested dtype. In img2img, provided latents skipprepare_latents()entirely, so they also skip shape validation and scheduler scaling.Impact:
Half-precision pipelines fail with dtype mismatch when users pass normal float32 latents. Img2img can fail later inside
PriorTransformerwith cryptic batch/shape errors instead of raising at input validation.Reproduction:
Relevant precedent:
Flux2 casts provided latents to both device and dtype:
diffusers/src/diffusers/pipelines/flux2/pipeline_flux2.py
Line 644 in 0f1abc4
Suggested fix:
For img2img, call
prepare_latents(...)unconditionally, as the text pipeline does, so provided latents get the same validation/scaling path.Issue 3: Fresh
ShapERenderermesh output has an empty marching-cubes lookup tableAffected code:
diffusers/src/diffusers/pipelines/shap_e/renderer.py
Lines 489 to 495 in 0f1abc4
diffusers/docs/source/en/using-diffusers/shap-e.md
Lines 135 to 150 in 0f1abc4
Problem:
MeshDecoderinitializescasesandmasksto zeros. The conversion script populates those buffers, but the runtime constructor does not. A renderer created from config or in fast tests cannot produce mesh geometry.Impact:
output_type="mesh"is documented, but fresh/local renderer construction produces empty meshes and can later fail with empty texture batches. Tests do not catch this path.Reproduction:
Relevant precedent:
The conversion script already has the lookup-table builder:
diffusers/scripts/convert_shap_e_to_diffusers.py
Lines 830 to 889 in 0f1abc4
Suggested fix:
Move the marching-cubes table generation into runtime code, or store a static table used by
MeshDecoder.__init__(). Keep loading checkpoint buffers for backwards compatibility, but the default constructor should initialize functionalcasesandmasks.Issue 4:
decode_to_image()drops remainder rays and fails for manyframe_sizevaluesAffected code:
diffusers/src/diffusers/pipelines/shap_e/renderer.py
Lines 921 to 944 in 0f1abc4
Problem:
n_batches = rays.shape[1] // ray_batch_sizeuses floor division. If total rays are smaller thanray_batch_size,imagesremains empty. If total rays are not exactly divisible, the tail rays are dropped and the final.view(...)shape is invalid.Impact:
Users can pass arbitrary
frame_size, but many sizes fail at render time. Current tests only use latent output in fast tests andframe_size=64in nightly tests, so this is uncovered.Reproduction:
Relevant precedent:
No duplicate found.
Suggested fix:
Issue 5: Renderer sampling resets the global PyTorch RNG
Affected code:
diffusers/src/diffusers/pipelines/shap_e/renderer.py
Lines 393 to 400 in 0f1abc4
diffusers/src/diffusers/pipelines/shap_e/renderer.py
Lines 456 to 460 in 0f1abc4
Problem:
StratifiedRaySampler.sample()callstorch.manual_seed(0)inside production rendering. This mutates global RNG state, and the adjacent comment is explicitly temporary/debug context.Impact:
Calling Shap-E rendering changes the caller’s global RNG sequence. It can also make each ray batch reuse the same sampling pattern.
Reproduction:
Relevant precedent:
No duplicate found.
Suggested fix:
Thread a local
torch.Generatorthroughdecode_to_image(),render_rays(),StratifiedRaySampler.sample(),ImportanceRaySampler.sample(), andsample_pmf(). If snapshot stability is required, seed that local generator with0without touching global state.Issue 6:
return_dict=Falseis ignored for latent outputsAffected code:
diffusers/src/diffusers/pipelines/shap_e/pipeline_shap_e.py
Lines 313 to 314 in 0f1abc4
diffusers/src/diffusers/pipelines/shap_e/pipeline_shap_e_img2img.py
Lines 300 to 301 in 0f1abc4
Problem:
Both pipelines return
ShapEPipelineOutputimmediately foroutput_type="latent", before the sharedif not return_dictbranch.Impact:
The public
return_dict=Falsecontract changes based on output type.Reproduction:
Relevant precedent:
Flux keeps latent handling before the final
return_dictbranch:diffusers/src/diffusers/pipelines/flux/pipeline_flux.py
Lines 1003 to 1017 in 0f1abc4
Suggested fix:
Issue 7:
DifferentiableProjectiveCamera.resize_image()cannot construct the resized cameraAffected code:
diffusers/src/diffusers/pipelines/shap_e/camera.py
Lines 104 to 118 in 0f1abc4
Problem:
DifferentiableProjectiveCamerarequiresshape, butresize_image()omits it when constructing the replacement camera.Impact:
The camera helper returned by
create_pan_cameras()exposes a broken resize method.Reproduction:
Relevant precedent:
No duplicate found.
Suggested fix:
Issue 8: Slow tests are missing, and offload coverage is skipped
Affected code:
diffusers/tests/pipelines/shap_e/test_shap_e.py
Lines 225 to 231 in 0f1abc4
diffusers/tests/pipelines/shap_e/test_shap_e_img2img.py
Lines 248 to 254 in 0f1abc4
Problem:
The target has fast tests and nightly integration tests, but no
@slowtests. Both sequential CPU offload tests are explicitly skipped with “Key error is raised with accelerate.”Impact:
RUN_SLOWdoes not exercise Shap-E, mesh output is untested, non-latent rendering is only covered nightly, and offload behavior is known-uncovered despite being in scope for pipeline review.Reproduction:
Relevant precedent:
No duplicate found.
Suggested fix:
Add
slowimports and@slowcoverage for at least one pretrained text-to-3D and img2img run, plus a smalloutput_type="mesh"assertion. Unskip or replace the sequential CPU offload tests with a current accelerate-compatible offload regression test.