Skip to content

spec(MODEL-MM-indextts2): scope IndexTTS-2.5, our first audio-generating lane (#634) - #637

Merged
localai-bot merged 12 commits into
mainfrom
row/MODEL-TTS-INDEXTTS-2-5
Aug 13, 2026
Merged

spec(MODEL-MM-indextts2): scope IndexTTS-2.5, our first audio-generating lane (#634)#637
localai-bot merged 12 commits into
mainfrom
row/MODEL-TTS-INDEXTTS-2-5

Conversation

@localai-bot

@localai-bot localai-bot commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator

Scopes #634. Stacked on
#635
; retargets to main when
that merges.

Why

vLLM-Omni supports IndexTTS-2.5 and we had no record of it. Every audio path we
ship CONSUMES audio (Parakeet, Voxtral, audio_processor.cpp); nothing
synthesizes it, and /v1/audio/speech does not exist. Scoping only, no product
code.

Two architectures, so two rows

IndexTTS2TalkerForConditionalGeneration (indextts2/indextts2_talker.py) and
IndexTTS2S2MelDecoder (indextts2/indextts2_s2mel_decoder.py), at
registry.py @ bbe6ccc512a404a2df8c977ea29003002f2683e8. Stage 0 is a ~0.8B
GPT-2 AR talker; stage 1 is EnhancedCodec (2.5 replaces RepCodec,
use_gpt_latent=false) then S2Mel CFM/DiT then BigVGAN at 22.05 kHz. ~6 GB VRAM.

Two reuse claims CORRECTED after LTX-2.5 (#641) merged

The first version of this spec said BigVGAN "needs generalizing out of the
minimax_h3_* namespace, which converges with #435". Both halves were wrong:

  • LTX-2.5 generalized the video engine (vllm::multimodal::VideoEngine), not
    the vocoder.
  • The BigVGAN 1-D core (Conv1d, ConvTranspose1d, pads, Snake/SnakeBeta, alias-free
    Activation1d) is already shared between H3 and LTX-2.5, published from
    minimax_h3.h and gated by both suites. ltx2_audio_vae.cpp:223-230 records
    why it was not copied: a second copy of the alias-free trim geometry "goes
    wrong quietly, because each copy keeps its own green gate while the two audio
    VAEs drift apart."

IndexTTS-2.5 would be the third consumer, so W1 shrank from "generalize" to
"relocate that shared core to a neutral home", with a live precedent to follow.
Two mel front-ends now exist as well, and which one w2v-bert-2.0 needs is flagged
as a W3 question rather than assumed.

Three things the spec pins down

  • Reference audio is mandatory. No text-only synthesis upstream, so
    w2v-bert-2.0 + MaskGCT + CAMPPlus are required port surface.
  • No token-exact e2e gate is possible. A seed controls both AR sampling and
    per-request CFM noise, and upstream does not guarantee a bit-identical waveform
    across differing batch composition. Binding gate: per-stage numerics vs the
    checkpoint's own remote code plus token-exact mel codes at fixed seed / c1 /
    fixed batch composition, with an ASR-round-trip and speaker-similarity ratchet
    bounding max relative error from BOTH sides. Not a correlation gate
    (scale-invariant), not a count-based tolerance (bounds nothing).
  • We mirror vLLM-Omni, which itself deviates from IndexTeam (plain vLLM
    sampling, not num_beams=3). Recorded, not silently inherited. Also: the two
    upstream doc surfaces disagree on language support, so nothing reaches
    docs/FEATURES.md until indextts2_5.yaml settles it, and the checkpoint is
    under a custom bilibili-model-license.

INVENTORIED, not SPIKE

Both rows are unclaimed and blocked on the UNPINNED vllm-omni oracle
(.agents/oracles/vllm-omni.md: gateable = no, evidence = #633). SPIKE
would owe a CLAIM-* owner they do not have.

Merge note for the reviewer

model-matrix.md auto-merged into a broken file — two Total rows and prose
reading "plus 12 rows ... = 338 total. Those 10 are", because main had
restructured the same paragraph. Discarded and rebuilt on main: 366 -> 368
architecture rows, rollup 370 -> 372, INVENTORIED 321 -> 323. A follow-up commit
also restores the ENGINE_ROWS = 154 this branch inherits from its base, which
the wholesale resolution had wiped while the matrix still carried 154 rows — the
gate caught it and was right to.

Gates

agent-preflight.sh green except test_cpu_x86_llamacpp_floor, exit 4
(NO_QUIET_WINDOW) at load 74-127 while another session builds LTX-2.5 on the
same box; it never measured anything. Record suite 54/54.

Adversary and others added 10 commits August 13, 2026 14:29
…entory that says otherwise (#633)

FOLLOWING_AGENTS_PROTOCOL

The pin protocol covers one repository. Every architecture living only in
`vllm-project/vllm-omni` therefore has no oracle this protocol can NAME — not a
hard comparison, an unavailable one. Two rows already carry it as their blocker:
`minimax-h3.md:205-208` ("a vllm-omni pin is a prerequisite for W3+") and the H3
row in `model-matrix.md`, which closes with "OPEN: there is no vllm-omni parity
PIN". #435 (LTX-2.5) is the third.

The surface is ~40 modules wide, not three rows: the whole TTS family (IndexTTS2,
Fish Speech, GLM-TTS, Ming-TTS, MOSS-TTS, Qwen3-TTS, VoxCPM2, Voxtral-TTS, Higgs
Audio v2/v3, Step Audio2, CosyVoice3, OmniVoice) plus Qwen2.5/3-Omni, Aura-Omni,
Ming-Flash-Omni and Hunyuan Image3/Video. So this is also a RECORDS defect:
`model-matrix.md` claims an EXHAUSTIVE architecture inventory of 329 rows, and
that claim is scoped to one repository without saying so.

Design, in the spec: a SECOND pin block rather than a second value of the first.
vllm-omni requires vLLM 0.27.0+ against our 0.26.0.dev0 core pin, so the two are
allowed to disagree, and `vllm_core_commit_used` records which core commit the
omni oracle actually ran against. Two rules follow — an omni-gated number is
labeled with BOTH commits, and is never evidence about the core pin's surface.
Gateability stays per ARCHITECTURE: the oracle must build, run and EMIT OUTPUT
for that model, because `assert_oracle_commit` exists precisely because an oracle
once resolved and was a rollback.

The spec answers the question it would otherwise leave to be discovered: an omni
pin advance does NOT re-open the vLLM-side binding grids, conditional on
isolation (own virtualenv, no core-pinned row citing an omni number, no mutation
of ${VLLM_SOURCE} or the environment the core pin measures itself from). Fail any
condition and the denominator's own dependency tree moved, so it is re-validated
as a core sync cycle.

W1-W2 (the `upstream-sync.md` block schema, concept and rules) land here. W3-W5
are owed and unclaimed: the checker with its five RED-first assertions, the
measured pin values, and a per-architecture gateability demonstration. The pin
values are deliberately NOT invented here — they need an eligible host, and an
unavailable value stays PENDING rather than becoming an assumption.

`ENGINE_ROWS` 152 -> 153 for a genuinely new protocol row, never to make a
failing transition pass. Gates: `agent-preflight.sh` and `--staged` both green.

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
FOLLOWING_AGENTS_PROTOCOL

Two keyed-record conflicts, both resolved by taking the target branch version
WHOLESALE and reapplying the scoped edit, per AGENTS.md §Records.

`main` had independently bumped `ENGINE_ROWS` 152 -> 153 for `SERVE-RECIPE-ARGS`
while this branch bumped the same constant 152 -> 153 for `ENG-UPSTREAM-OMNI-PIN`.
Both rows are real and neither replaces the other, so the merged value is 154 —
taking either side's 153 would have silently dropped one row while leaving the
matrix looking consistent. The `Serving, API, CLI, library` rollup is merged the
same way: 32 -> 34 rows, main's +1 ACTIVE and this branch's +1 READY both kept.

Unrelated keys verified byte-for-byte: the whole engine-matrix diff against
origin/main is this row plus those two rollup cells, and the roadmap issue table
carries no duplicate issue key after its automerge.

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
…top overclaiming the inventory gap (#633)

FOLLOWING_AGENTS_PROTOCOL

Two corrections found while writing the IndexTTS-2.5 row against this spec.

The spec cited `a4ea67a2` as the one source audit of vllm-omni. `main` has since
landed a second, newer one: #609/#610 anchor their omni rows to
`bbe6ccc512a404a2df8c977ea29003002f2683e8`. Both are now cited, with the point
that neither is a pin — a source read establishes what EXISTS, never what RUNS,
which is the whole distinction this row turns on.

The spec also said the omni surface is "uninventoried and uncounted". That was
true when written and is no longer: those same PRs rowed several omni
architectures explicitly as out-of-repo. The residual defect is narrower and the
text now says the narrower thing — the document's scope sentence still does not
state the boundary those rows work around, and each of them repeats the same
missing-pin blocker in its evidence cell.

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
…ing lane (#634)

FOLLOWING_AGENTS_PROTOCOL

vLLM-Omni supports IndexTTS-2.5 (`vllm-omni serve IndexTeam/IndexTTS-2.5 --omni`)
and we have no record of it at all. Every audio path we ship CONSUMES audio
(Parakeet, Voxtral, `audio_processor.cpp`); nothing synthesizes it, and
`/v1/audio/speech` does not exist. This scopes the port; it implements nothing.

Upstream registers TWO architectures, so this rows both rather than collapsing
them: `IndexTTS2TalkerForConditionalGeneration` (`indextts2/indextts2_talker.py`)
and `IndexTTS2S2MelDecoder` (`indextts2/indextts2_s2mel_decoder.py`), at
`registry.py` @ `bbe6ccc512a404a2df8c977ea29003002f2683e8` — the same anchor the
Moss-TTS rows use. Stage 0 is a ~0.8B GPT-2 AR talker; stage 1 is EnhancedCodec
(2.5 replaces RepCodec, `use_gpt_latent=false`) -> S2Mel CFM/DiT -> BigVGAN at
22.05 kHz, ~6 GB VRAM.

The reuse is real and is why this is worth scoping now: BigVGAN is ALREADY ported
(`minimax_h3_audio_vae.cpp`, gated 4.2e-9 vs the checkpoint's own remote code),
as are WAV serialization, the flow-matching denoise loop, a Conformer encoder
(w2v-bert-2.0 is a Conformer) and the `/v1/videos` runner seam. `opt.cpp` is the
GPT-2 family, so the talker is an additive delta rather than a fresh transformer.

Three things the spec pins down rather than leaving to be discovered:

- REFERENCE AUDIO IS MANDATORY. Upstream states IndexTTS-2 has no text-only
  synthesis, so w2v-bert-2.0 + MaskGCT + CAMPPlus are required port surface, not
  an optional voice-cloning extra.
- THE GATE CANNOT BE TOKEN-EXACT END TO END. A seed controls both AR sampling and
  per-request CFM noise, and upstream does not guarantee a bit-identical waveform
  across differing concurrent batch composition. So: binding per-stage numerics
  vs the checkpoint's own remote code plus token-exact mel codes at fixed seed /
  c1 / fixed batch composition, with an ASR-round-trip + speaker-similarity
  ratchet that bounds max relative error from BOTH sides. Explicitly not a
  correlation gate (scale-invariant) and not a count-based tolerance.
- WE MIRROR vLLM-OMNI, WHICH ITSELF DEVIATES from IndexTeam: upstream's stage 0
  uses plain vLLM sampling, not the official `num_beams=3`. Recorded, not
  silently inherited. Also recorded: the two upstream doc surfaces disagree on
  language support, so nothing reaches `docs/FEATURES.md` until `indextts2_5.yaml`
  settles it, and the checkpoint is under a custom bilibili-model-license.

Both rows land `INVENTORIED`, not `SPIKE`: they are unclaimed and hard-blocked on
#633 (no vllm-omni pin, and vllm-omni wants vLLM 0.27.0+ against our 0.26.0.dev0).
`SPIKE` would owe a `CLAIM-*` owner these rows do not have, and the checklist
legend reserves a per-row mark for engaged rows — so they sit in the rollup tail
with the spec linked from their Spike cell. Rollup reconciled in the same commit:
INVENTORIED 321 -> 323, total 369 -> 371, checklist prose 336 -> 338 with engaged
unchanged at 45 and the tail 291 -> 293.

Quantization is bf16-only for v1 with every other arm refused at load naming the
missing piece and recorded as owed — vLLM-Omni ships no quantized IndexTTS arm,
and the model fits GB10 unquantized.

Gates: `agent-preflight.sh` and `--staged` green.

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
FOLLOWING_AGENTS_PROTOCOL

This row is STACKED on the pin row, not independent of it: the IndexTTS-2.5 spec
cross-references `specs/upstream-omni-pin.md`, and #634 is hard-blocked on #633.
Merging the base branch in keeps those links resolvable in this tree rather than
landing a spec whose central dependency is a dangling reference.

No conflict; the only incoming change is the pin spec's own correction commit.

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
…d-bearing (#633)

FOLLOWING_AGENTS_PROTOCOL

`check-pr-size.py` correctly refused the branch: a change to
`scripts/check-agent-record.py` owes semantic mutation evidence in
`tests/scripts/test_agent_record.py`, and the count bump had none.

The new assertion names BOTH rows the constant now covers, which matters more
here than in the #117 and #606 precedents it copies, because this bump COLLIDED.
`main` took the constant 152 -> 153 for `SERVE-RECIPE-ARGS` while this branch
took the same 152 -> 153 for `ENG-UPSTREAM-OMNI-PIN`, so both sides read 153 and
the merge looked clean. Keeping either 153 would have dropped a real row while
leaving the matrix internally consistent.

RED-first, against exactly that bad merge: drop the omni-pin row, rewind
`ENGINE_ROWS` to 153 and rewind the serving rollup, so every count agrees with
every other count. The full suite then reports 53 tests with ONE failure, and it
is this test — nothing else in the record can see a row that vanished into a
matching number. Scratch copy restored byte-for-byte, verified by comparison.

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
…het evidence)

FOLLOWING_AGENTS_PROTOCOL

Picks up the engine-ratchet mutation evidence check-pr-size required on the base
branch, so this stacked branch carries it too.

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
…load-bearing (#634)

FOLLOWING_AGENTS_PROTOCOL

The mutation evidence `check-pr-size.py` requires for the `check-agent-record.py`
MODEL count change, mirroring the #609/#610 assertion it copies.

IndexTTS-2.5 is registered by vLLM-Omni as TWO architectures (talker + S2Mel
decoder), so it moves the pin by two rather than one. That is the specific
hazard: a port described in prose as "a model" is exactly the shape that lands
ONE row with a bump of TWO, and no count can tell that apart from two rows
landing. Both rows are named, and both are asserted `INVENTORIED` rather than
`SPIKE` — unclaimed and blocked on #633, where `SPIKE` would owe a `CLAIM-*`
owner they do not have.

RED-first against that exact half-landed shape: drop the S2Mel row, rewind the
MODEL pin to 370, and rewind the rollup and the checklist prose to match, so
every number in the record agrees with every other number. The suite reports 54
tests with ONE failure and it is this test. Scratch copy restored byte-for-byte,
verified by comparison.

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
…t superseded

FOLLOWING_AGENTS_PROTOCOL

Three keyed-record conflicts, all resolved by taking main WHOLESALE and
reapplying the scoped edit, per AGENTS.md §Records. The automatic merge of
`model-matrix.md` was the instructive one: it produced a file with TWO `Total`
rows and prose reading "plus 12 rows ... = 338 total. Those 10 are", because
main had restructured the same paragraph (326 at the pin -> 355, and a separate
rollup total). Discarded and rebuilt: 366 -> 368 architecture rows, rollup
370 -> 372, INVENTORIED 321 -> 323, both IndexTTS rows re-added.

`docs/FEATURES.md` and the MODEL count in `check-agent-record.py` were rebuilt
the same way, so main's LTX-2.5 line and its 363/370 comment history survive
intact and this branch's edits sit on top.

The ratchet-test docstring now reads 370 -> 372 rather than 369 -> 371, so the
assertion's stated reason matches the number it guards.

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
…ed (#634)

FOLLOWING_AGENTS_PROTOCOL

Resolving the `check-agent-record.py` conflict by taking main WHOLESALE also took
main's `ENGINE_ROWS = 153`, discarding the 154 this branch inherits from its base
`row/ENG-UPSTREAM-OMNI-PIN`. The matrix still carried 154 rows, so the gate read
"154 engine rows; expected 153" and was right to.

Restored verbatim from the base branch, justification comment included, so the
two branches state the same number for the same reason. Exactly the failure mode
"take the target version wholesale" is supposed to be paired with re-applying
EVERY scoped edit, not just the ones the conflict marker pointed at.

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
mudler added 2 commits August 13, 2026 21:05
… neither exists (#634)

FOLLOWING_AGENTS_PROTOCOL

The spec carried the serving surface as a one-line W6, which understated it. A
capability that is not reachable through the shared surface is not done, so the
seam and the ABI are scope, and the spec now says what shape they take.

VERIFIED state, not assumed: `include/vllm.h` is at `VLLM_ABI_VERSION 18` and
contains NO speech or TTS entry point; `ApiServer` registers chat, completions,
embeddings, models, `/v1/audio/transcriptions`, `/v1/videos` and
`/v1/videos/sync`; `audio/speech` and `audio/voices` have ZERO hits across
`src/`, `include/` and `examples/`. Every audio route we serve consumes audio.
None produces it.

The precedent is the video lane, which solved this for a generative modality:
an abstract `vllm::multimodal::VideoEngine` (`video_engine.h:135`), the assembly
library-owned behind `vllm_video_*` ABI entries, `/v1/videos` routed through that
same seam, and the example reduced to a thin `vllm.h` client. H3 and LTX-2.5 both
sit behind it, which is the evidence the shape survives a second lane.

So this lane mirrors it rather than inventing one: a `SpeechEngine` seam whose
first implementation is IndexTTS-2.5 but which must not be its ONLY possible one
(the omni TTS family is ~10 further architectures), ABI v19 with `vllm_synthesize`
named symmetrically to the existing `vllm_transcribe`, the two routes through the
same seam, and refusal-by-name as part of the surface. W6 splits into W6a (seam +
ABI + `test_capi`) and W6b (routes + example).

Neither needs the vllm-omni pin: a seam and an ABI are gateable against their own
contract, so they are NOT deferred behind #633 the way the e2e and speed claims
are.

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
FOLLOWING_AGENTS_PROTOCOL

#635 landed as squash `782264c0c`, so the pin content this branch carried as its
own commits arrives on main as a DIFFERENT commit. That produced an add/add
conflict on `specs/upstream-omni-pin.md` plus content conflicts on
`upstream-sync.md` and the roadmap issue table.

Resolved by taking MAIN's version for everything the pin PR owned — the squash is
the merged truth for those files, and keeping this branch's pre-squash copies
would resurrect a superseded draft — then reapplying only this row's own scoped
edit, the #634 issue-table entry.

Verified on the merged tree rather than inferred: check-agent-record,
check-model-checklist, check-oracle-pins, check-doc-checkpoint,
check-public-doc-tables, check-now-current and the 54-case record suite all green,
with `ENGINE_ROWS = 154` (from the merged pin row) and MODEL = 372 (this row's
two architectures) both intact.

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
@localai-bot
localai-bot changed the base branch from row/ENG-UPSTREAM-OMNI-PIN to main August 13, 2026 21:13
@localai-bot
localai-bot merged commit b338e93 into main Aug 13, 2026
@localai-bot
localai-bot deleted the row/MODEL-TTS-INDEXTTS-2-5 branch August 13, 2026 21:14
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