Skip to content

feat(MODEL-MM-indextts2): convert the checkpoints OFFLINE, and refuse to read pickle in the engine (#634) - #742

Merged
localai-bot merged 1 commit into
mainfrom
row/MODEL-MM-indextts2-convert
Aug 14, 2026
Merged

feat(MODEL-MM-indextts2): convert the checkpoints OFFLINE, and refuse to read pickle in the engine (#634)#742
localai-bot merged 1 commit into
mainfrom
row/MODEL-MM-indextts2-convert

Conversation

@localai-bot

Copy link
Copy Markdown
Collaborator

feat(MODEL-MM-indextts2): convert the checkpoints OFFLINE, and refuse to read pickle in the engine (#634)

Upstream ships .pth: a ZIP around a Python pickle. Loading it needs a reader
this tree does not have, and the answer is not to write one. Pickle executes
arbitrary code by construction, so a reader in the engine would run an
attacker-controllable program inside the process that serves users, and every
other lane here already loads safetensors or GGUF. The conversion therefore
happens offline, once, and the engine loads the result through the reader it
already has.

scripts/convert-indextts2-checkpoint.py flattens the nested state dicts with
'.', which is exactly the naming indextts2_pth_manifest.json records, so the
converted names ARE the manifest's names and the manifest can check the
conversion. Run against the real checkpoint on the NAS, all three files matched
their recorded tensor counts: 456, 972, 284.

It also measured something the manifest could only hint at. codec.pth is
75% optimizer state -- 729 of its 972 tensors are training residue -- and
dropping it takes that file from 579.16 MiB to 192.99 MiB. The drop is reported
with a count, never silent, and the prefix is gated from BOTH sides: every
optimizer key in the manifest must match it, and no weight in gpt.pth or
s2mel.pth may. A prefix widened from "optimizer." to "opt" would quietly eat
model weights, which is exactly the mutation that proves the second check.

The conversion needs torch and 4 GiB of weights, so CI cannot run it.
tests/scripts/test_indextts2_convert.py holds the part where a silent mistake
is unrecoverable -- which tensors survive, under which names -- using fakes,
with neither torch nor the checkpoint, because a dropped weight looks exactly
like a weight that was never there. It also asserts the flattener survives the
ints, strings and Nones that sit beside weights in a .pth.

8 cases. Five mutations, five caught, script verified byte-identical after:
the drop prefix widened to "opt", the drop prefix emptied, the flattener joining
with '/' instead of '.', the flattener crashing on non-tensor leaves, and one
source silently omitted.

This unblocks the checkpoint loader; it is not the loader, and there is still no
render.

Issue: #634.

🤖 Generated with Claude Code

… to read pickle in the engine (#634)

Upstream ships `.pth`: a ZIP around a Python pickle. Loading it needs a reader
this tree does not have, and the answer is not to write one. Pickle executes
arbitrary code by construction, so a reader in the engine would run an
attacker-controllable program inside the process that serves users, and every
other lane here already loads safetensors or GGUF. The conversion therefore
happens offline, once, and the engine loads the result through the reader it
already has.

`scripts/convert-indextts2-checkpoint.py` flattens the nested state dicts with
'.', which is exactly the naming `indextts2_pth_manifest.json` records, so the
converted names ARE the manifest's names and the manifest can check the
conversion. Run against the real checkpoint on the NAS, all three files matched
their recorded tensor counts: 456, 972, 284.

It also measured something the manifest could only hint at. `codec.pth` is
**75% optimizer state** -- 729 of its 972 tensors are training residue -- and
dropping it takes that file from 579.16 MiB to 192.99 MiB. The drop is reported
with a count, never silent, and the prefix is gated from BOTH sides: every
optimizer key in the manifest must match it, and no weight in `gpt.pth` or
`s2mel.pth` may. A prefix widened from "optimizer." to "opt" would quietly eat
model weights, which is exactly the mutation that proves the second check.

The conversion needs torch and 4 GiB of weights, so CI cannot run it.
`tests/scripts/test_indextts2_convert.py` holds the part where a silent mistake
is unrecoverable -- which tensors survive, under which names -- using fakes,
with neither torch nor the checkpoint, because a dropped weight looks exactly
like a weight that was never there. It also asserts the flattener survives the
ints, strings and Nones that sit beside weights in a `.pth`.

8 cases. Five mutations, five caught, script verified byte-identical after:
the drop prefix widened to "opt", the drop prefix emptied, the flattener joining
with '/' instead of '.', the flattener crashing on non-tensor leaves, and one
source silently omitted.

This unblocks the checkpoint loader; it is not the loader, and there is still no
render.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
@localai-bot
localai-bot merged commit 25861b6 into main Aug 14, 2026
@localai-bot
localai-bot deleted the row/MODEL-MM-indextts2-convert branch August 14, 2026 11:37
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.

2 participants