Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion families/personaplex/reference/adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ def run() -> list[Any]:
tokens = self.generator.step(encoded[:, :, index:index + 1])
if tokens is None:
continue
# Decode the returned stream once; the other codec is only needed for official warmup.
chunks.append(self.mimi.decode(tokens[:, 1:9])[0, 0].float())
self.other_mimi.decode(tokens[:, 1:9])
if len(chunks) >= frames:
return chunks
return chunks
Expand Down
70 changes: 70 additions & 0 deletions families/personaplex/tests/test_reference_decode_work.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

"""The timed Native task decodes the audio it returns, once per frame."""

from __future__ import annotations

import sys
from types import ModuleType, SimpleNamespace

import numpy as np
import pytest

from families.personaplex.reference.adapter import Adapter


def test_native_speech_does_not_decode_discarded_audio(monkeypatch, tmp_path):
torch = pytest.importorskip("torch")

class Codec:
sample_rate = 24_000

def __init__(self):
self.decode_calls = 0

def reset_streaming(self):
pass

def decode(self, tokens):
self.decode_calls += 1
value = float(tokens[0, 0, 0])
return torch.tensor([[[value, value + 0.5]]])

class Generator:
def reset_streaming(self):
pass

def step(self, encoded):
return encoded[:, :1].expand(1, 17, 1)

# Exercise the real invoke loop without loading a checkpoint or using CUDA.
lm = ModuleType("moshi.models.lm")
lm._iterate_audio = lambda *args, **kwargs: iter(())
lm.encode_from_sphn = lambda *args, **kwargs: iter(
[torch.tensor([[[1, 2, 3]]], dtype=torch.int64)]
)
monkeypatch.setitem(sys.modules, "moshi.models.lm", lm)

adapter = Adapter.__new__(Adapter)
adapter.mimi = Codec()
adapter.other_mimi = Codec() # retained for the pinned official warmup
adapter.generator = Generator()
adapter.frame_size = 1920
adapter.host = SimpleNamespace(
required=lambda request, name: request[name],
load_audio=lambda *args: np.zeros(5760, dtype=np.float32),
timed=lambda run: (run(), 1.0),
tensor_observation=lambda audio, base: {"audio": audio},
invocation=lambda observation, ms: (observation, ms),
)

observation, _ = adapter.invoke(
{"audio_path": "input.wav", "max_new_tokens": 2}, tmp_path / "result"
)

np.testing.assert_array_equal(observation["audio"], [1.0, 1.5, 2.0, 2.5])
assert observation["output_tokens"] == 2
assert observation["sample_rate"] == 24_000
assert adapter.mimi.decode_calls == 2
assert adapter.other_mimi.decode_calls == 0
Loading