Skip to content

Fix weights_only=True load failure for bundled voice presets - #440

Open
Ammar Hashmi (ammar-iitm) wants to merge 2 commits into
microsoft:mainfrom
ammar-iitm:fix/voice-preset-weights-only-load
Open

Ammar Hashmi (ammar-iitm) wants to merge 2 commits into
microsoft:mainfrom
ammar-iitm:fix/voice-preset-weights-only-load

Conversation

@ammar-iitm

Copy link
Copy Markdown

Summary

  • Fixes App/Demo exception since fix: use weights_only=True (CWE-502) #392: demo/web/app.py crashes on startup on recent PyTorch (not ROCm-specific — reproduced on CPU with torch 2.14 / transformers 5.16) with:
    _pickle.UnpicklingError: Weights only load failed. ...
    Can only SETITEMS for dict, collections.OrderedDict, collections.Counter, but got <class 'transformers.modeling_outputs.BaseModelOutputWithPast'>
    
  • Root cause (confirmed in the issue by Zhiliang Peng (@pengzhiliang)): the bundled voice preset .pt files pickle BaseModelOutputWithPast/DynamicCache objects. Even with safe_globals allowing those classes, torch's weights-only unpickler only supports the SETITEMS restore path for plain dict/OrderedDict/Counter, not subclasses like these, so the load fails regardless of safe_globals.
  • This is the "update the voice preset format/loading path" fix mentioned in the issue thread, rather than the weights_only=False stopgap.

Changes

  • Re-saved all 25 bundled presets in demo/voices/streaming_model/*.pt so each stores only plain dict/list/int/Tensor values (last_hidden_state tensor, plus past_key_values as {seen_tokens, key_cache: [...], value_cache: [...]}) instead of pickling the BaseModelOutputWithPast/DynamicCache objects directly.
  • StreamingTTSService._ensure_voice_cached in demo/web/app.py now loads that plain structure with torch.load(..., weights_only=True) (no safe_globals needed) and reconstructs BaseModelOutputWithPast/DynamicCache in code before caching.

Verification

Done in an isolated venv (torch 2.14.0 cpu, transformers 5.16.1) since I don't have a GPU here:

  • Reproduced the original crash against the pre-change .pt files with the exact safe_globals + weights_only=True call from app.py.
  • For every one of the 25 presets: loaded the old format with weights_only=False (trusted, since these are the repo's own bundled assets), converted to the new plain format, saved, then reloaded with weights_only=True and asserted the reconstructed last_hidden_state, seen_tokens, key_cache, and value_cache tensors are bit-for-bit identical (torch.equal) to the originals, before overwriting each file.
  • Ran the new _ensure_voice_cached logic verbatim against all 25 converted files — all load cleanly under weights_only=True with no safe_globals.
  • Confirmed copy.deepcopy() on the reconstructed prefilled_outputs (used downstream in stream()/_run_generation) still works.
  • python3 -m py_compile demo/web/app.py passes.

Not verified: full end-to-end streaming generation on GPU (no GPU available in this environment) — would appreciate a maintainer or the original reporter confirming the demo now starts cleanly end-to-end.

🤖 Generated with Claude Code

https://claude.ai/code/session_016fM2LafALmBK16KnFWjsp7

Ammar Hashmi and others added 2 commits September 5, 2026 02:44
demo/web/app.py crashed on startup with "Weights only load failed"
on recent PyTorch (not ROCm-specific) because the bundled voice
preset .pt files pickled BaseModelOutputWithPast/DynamicCache
objects. Even with safe_globals allowing those classes, torch's
weights-only unpickler can't reconstruct them: it only supports the
SETITEMS restore op for plain dict/OrderedDict/Counter, and these
are subclasses that don't qualify (confirmed root cause, see microsoft#392).

Re-save the 25 bundled presets so each stores only plain
dict/list/int/Tensor values (last_hidden_state tensor + past KV
cache tensors/seen_tokens), which the default weights-only
unpickler already supports. _ensure_voice_cached now loads that
plain structure with weights_only=True (no safe_globals needed)
and reconstructs the BaseModelOutputWithPast/DynamicCache objects
in code.

Verified in an isolated venv: every converted preset round-trips
bit-for-bit identical to the original tensors, loads under
weights_only=True, and survives the copy.deepcopy() used downstream
in stream().

Fixes microsoft#392

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016fM2LafALmBK16KnFWjsp7
The conversion script wrote replacement files via a temp file,
which picked up the default umask instead of the original mode.
No content change.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016fM2LafALmBK16KnFWjsp7
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

App/Demo exception since fix: use weights_only=True (CWE-502)

1 participant