Skip to content

Add EasyMagpie vLLM-Omni serving#15931

Open
vklimkov-nvidia wants to merge 2 commits into
NVIDIA-NeMo:mainfrom
vklimkov-nvidia:codex/migrate-easymagpie-vllm-omni
Open

Add EasyMagpie vLLM-Omni serving#15931
vklimkov-nvidia wants to merge 2 commits into
NVIDIA-NeMo:mainfrom
vklimkov-nvidia:codex/migrate-easymagpie-vllm-omni

Conversation

@vklimkov-nvidia

@vklimkov-nvidia vklimkov-nvidia commented Jul 21, 2026

Copy link
Copy Markdown
Member

Summary

  • Adds EasyMagpieTTS streaming inference on vLLM-Omni for NemotronTTS, using a Nemotron-H autoregressive talker with a per-codebook local transformer over a 25 fps spectral codec.
  • Implements two registered deployment topologies:
    • a talker-only pipeline for acoustic-token generation
    • a two-stage pipeline that connects the talker to a native stateful codec and produces 22.05 kHz waveform chunks
  • Supports offline synthesis, OpenAI-compatible whole-text HTTP serving through POST /v1/audio/speech, and incremental text/audio streaming through WS /v1/audio/speech/stream.
  • Includes checkpoint conversion, vLLM plugin registration, deployment configs, benchmark tools, notebooks, and focused unit tests.

Why

EasyMagpie generates stacked acoustic codes autoregressively and then decodes them into audio. Running both stages in vLLM-Omni provides batched, high-throughput inference while retaining progressive audio output for streaming and voice-agent use cases.

The conversion tool turns the training-time EasyMagpie and causal codec .nemo checkpoints into a self-contained model directory. It converts the model weights, precomputes the text-embedding lookup, bundles the native codec, saves the tokenizer, and optionally stores speaker embeddings. After conversion, serving requires vLLM/vLLM-Omni and this plugin package, but does not require NeMo at runtime.

The native codec keeps its causal history in vLLM-managed state pages, allowing each invocation to process only newly generated acoustic frames and emit asynchronous PCM chunks without replaying the full context.

Validation

  • Repository isort and Black checks pass.
  • pytest -q examples/tts/easymagpie_vllm_omni/tests: 70 passed, 1 skipped.
  • A real EasyMagpie talker and 25 fps codec conversion completed successfully on an RTX A6000.
  • Generated artifacts were validated for a consistent safetensors index, tokenizer and architecture metadata, with 956 talker tensors and 76 codec tensors.
  • Conversion imports for add_special_tokens, NemotronHConfig, ModelLoadConfig, and load_easy_magpie_model resolve from the Speech checkout.
  • The 128-request RTX A6000 reference at concurrency 32 measured:
    • model whole-text input: 67.75x RTF
    • HTTP whole-text service: 48.43x RTF
    • WebSocket incremental service: 44.12x RTF
    • zero playback underruns in both service modes

@copy-pr-bot

copy-pr-bot Bot commented Jul 21, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@github-actions github-actions Bot added the TTS label Jul 21, 2026
@vklimkov-nvidia
vklimkov-nvidia force-pushed the codex/migrate-easymagpie-vllm-omni branch from 376d038 to df4f340 Compare July 21, 2026 12:23
Signed-off-by: Viacheslav Klimkov <vklimkov@nvidia.com>
@vklimkov-nvidia
vklimkov-nvidia force-pushed the codex/migrate-easymagpie-vllm-omni branch from df4f340 to 8f85db5 Compare July 21, 2026 12:24
Comment thread examples/tts/easymagpie_vllm_omni/scripts/benchmark_model.py Fixed
Signed-off-by: Viacheslav Klimkov <vklimkov@nvidia.com>
@vklimkov-nvidia
vklimkov-nvidia marked this pull request as ready for review July 21, 2026 12:46
@vklimkov-nvidia
vklimkov-nvidia requested a review from a team as a code owner July 21, 2026 12:47
@blisc
blisc self-requested a review July 21, 2026 15:01
@vklimkov-nvidia vklimkov-nvidia changed the title Add EasyMagpie vLLM-Omni serving example Add EasyMagpie vLLM-Omni serving Jul 21, 2026
@blisc

blisc commented Jul 25, 2026

Copy link
Copy Markdown
Collaborator

/ok to test 0734cee

@blisc blisc left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall the PR looks good from my end. The immediate concerns are

  • How we package this component? It looks like it is currently constructed to be completely standalone from the rest of NeMo Speech which can be done.
  • On packaging, despite that decision, we need tests to run which means that for testing, the requirements need to be added to NeMo's pyproject.toml and tests need to be moved to the NeMo tests folder, and we need to make sure that they run.
  • The current implementation is slightly hard-coded which is fine for initial version, but we need much more strict config validation functions that explain what parameters will break the current implementation, and how we can update the implementation to allow future parameters

# pipeline: easymagpie_talker
#
# scripts/benchmark_model.py uses this deploy config by default.
pipeline: easymagpie_talker

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's make a better naming convention. Not sure that easymagpie_talker should be used for the pipeline without the codec. @paarthneekhara, what should we refer to the backbone+LT as?

@@ -0,0 +1,18 @@
# Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated to file, but we need to standard where we keep these implementations. Looks like you are putting it inside of examples, but speechlm2 keeps it inside of collections. Collections is probably where we want to put it since the pip packages ships with collections but not examples.

# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""NeMo-free EasyMagpie spectral codec implementation.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps instead of keeping a nemo-free implementation here, we ought to have a nn.Module base class that both will inherit.

Comment on lines +55 to +59
class _SiluActivation(nn.Module):
"""Module wrapper for ``F.silu``."""

def forward(self, x):
return F.silu(x)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a difference between nemo/collections/tts/modules/nemotron_h_decoder.py and automodel? Should we read this from the config instead of auto patching?

Comment on lines +70 to +73
if decoder_config.get("_target_") != expected_target:
raise ValueError(f"expected {expected_target}, got {decoder_config.get('_target_')}")
if not decoder_config.get("is_causal"):
raise ValueError("only the causal spectral codec decoder is supported")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like these checks should be more strict.

@github-actions

Copy link
Copy Markdown
Contributor

[🤖]: Hi @vklimkov-nvidia 👋,

We wanted to let you know that a CICD pipeline for this PR just finished successfully.

So it might be time to merge this PR or get some approvals.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants