Skip to content
Merged
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
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ All notable changes to this project will be documented in this file.

This project follows semantic versioning once published to PyPI.

## Unreleased

### Fixed

- Preserved exact digital silence in `lavasr-compat` instead of passing it through the model and
producing deterministic low-level output texture.
- Made `audio-super-res eval matrix` return a non-zero exit status when any matrix run fails, so
remote evidence workflows cannot report success for failed stability checks.

## 0.6.0 - 2026-07-12

### Added
Expand Down
4 changes: 2 additions & 2 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ evidence and a smaller number of well-supported paths.
| Evaluation | Full-reference, no-reference, downstream, listening, matrix, report, and bundle workflows are implemented. |
| Packaging | Source version is `0.6.0`; build, metadata, wheel, lint, type, and default test gates are available through Pixi. |

## Priority 0: Ship v0.6.0 Cleanly
## Completed: Ship v0.6.0 Cleanly

The immediate goal is a release-quality package, not another model backend.

Expand All @@ -24,7 +24,7 @@ Release-preparation status:
- [x] Run the complete local release checklist in [docs/RELEASE.md](docs/RELEASE.md).
- [x] Confirm CI on the merge commit and inspect the built wheel/sdist.
- [x] Run a fresh installed-wheel CLI smoke test outside the source tree.
- [ ] Tag `v0.6.0`, publish the GitHub release, and verify the trusted-publishing workflow and PyPI
- [x] Tag `v0.6.0`, publish the GitHub release, and verify the trusted-publishing workflow and PyPI
installation.

Release scope deliberately excludes new heavyweight evaluators, new model weights, and a new
Expand Down
14 changes: 14 additions & 0 deletions docs/ACCELERATORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -224,3 +224,17 @@ A fresh Colab Tesla T4 run was recorded on 2026-07-01 UTC at commit

This is historical evidence for the eager CUDA path, not a permanent performance baseline. Repeat
the workflow on the release commit before making new device/provider claims.

A v0.6.0 Colab CLI run on 2026-07-12 validated the published tag at commit
`5e4241c5ed7399b29d04bbfe39f04fdfd9f100dc` on the same Tesla T4 environment:

- real-weight download/verification and CUDA torch smoke passed;
- explicit CUDA device detection passed;
- four of five generated failure cases passed;
- exact digital silence exposed deterministic model output around `-75 dBFS` RMS and was correctly
reported as `silence_hallucination` by the eval harness;
- the follow-up exact-silence preservation fix passed all five cases on the same remote session,
with zero duration drift for the silence fixture.

The downloaded evidence archive belongs under ignored `runs/` or the associated GitHub issue/PR,
not in the source distribution.
3 changes: 3 additions & 0 deletions examples/colab_lavasr_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,9 @@ def main() -> int:
],
cwd=REPO_DIR,
)
matrix_manifest = json.loads((matrix_dir / "matrix.json").read_text(encoding="utf-8"))
if not matrix_manifest.get("passed"):
raise RuntimeError("LavaSR failure-case matrix reported one or more failed runs")
_write_summary(status="passed")
except Exception as exc:
error = f"{type(exc).__name__}: {exc}"
Expand Down
5 changes: 4 additions & 1 deletion src/audio_super_resolution/backends/lavasr_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,13 @@ def enhance(self, audio: np.ndarray, sample_rate: int, target_sample_rate: int)
device = resolve_device(self.config.device, supported_devices=LAVASR_CAPABILITY.accelerators)
_require_torch_runtime()

prepared_audio, was_mono = _prepare_lavasr_input(audio, sample_rate)
if not np.any(prepared_audio):
return _restore_lavasr_output(np.zeros_like(prepared_audio).T, was_mono=was_mono)
Comment on lines 117 to +122

import torch

model = self._load_model(resolved_weights, bundle_info, device=device)
prepared_audio, was_mono = _prepare_lavasr_input(audio, sample_rate)
waveform = torch.from_numpy(prepared_audio.T.copy()).to(device=device, dtype=torch.float32)

with torch.inference_mode():
Expand Down
2 changes: 1 addition & 1 deletion src/audio_super_resolution/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -602,7 +602,7 @@ def _run_eval_command(argv: list[str]) -> int:
),
)
print(f"Wrote eval matrix {args.output_dir / 'matrix.json'} ({manifest['run_count']} run(s))")
return 0
return 0 if manifest["passed"] else 1
if args.eval_command == "run":
model_cache_dir = args.model_cache_dir if args.model_cache_dir is not None else default_model_cache_dir()
manifest = run_eval_dataset(
Expand Down
21 changes: 21 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,27 @@ def test_cli_eval_matrix(tmp_path: Path, capsys) -> None:
assert {run["degrader"] for run in matrix["runs"]} == {"lowpass_4k", "mp3_32kbps"}


def test_cli_eval_matrix_returns_nonzero_when_a_run_fails(monkeypatch, tmp_path: Path, capsys) -> None:
monkeypatch.setattr(
"audio_super_resolution.cli.run_eval_matrix",
lambda **kwargs: {"passed": False, "run_count": 1},
)

exit_code = main(
[
"eval",
"matrix",
"--dataset",
str(tmp_path / "dataset"),
"--output-dir",
str(tmp_path / "matrix"),
]
)

assert exit_code == 1
assert "Wrote eval matrix" in capsys.readouterr().out


def test_cli_eval_matrix_compare_report_bundle_and_validate_dataset(tmp_path: Path, capsys) -> None:
evalset = tmp_path / "evalset"
assert (
Expand Down
30 changes: 30 additions & 0 deletions tests/test_lavasr_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,33 @@ def test_lavasr_backend_rejects_unsupported_precision_before_weight_resolution(t

with pytest.raises(ValueError, match="precision modes"):
backend.enhance(np.zeros(100, dtype=np.float32), 16000, 48000)


def test_lavasr_backend_preserves_exact_digital_silence(monkeypatch, tmp_path) -> None:
resolved_weights = object()
bundle_info = object()
monkeypatch.setattr(
"audio_super_resolution.weight_store.resolve_weights_for_spec",
lambda *args, **kwargs: resolved_weights,
)
monkeypatch.setattr(
"audio_super_resolution.backends.lavasr_validation.validate_lavasr_v2_weight_bundle",
lambda resolved: bundle_info,
)
monkeypatch.setattr("audio_super_resolution.backends.lavasr_compat.resolve_runtime_provider", lambda *args: None)
monkeypatch.setattr("audio_super_resolution.backends.lavasr_compat.resolve_device", lambda *args, **kwargs: "cuda")
monkeypatch.setattr("audio_super_resolution.backends.lavasr_compat._require_torch_runtime", lambda: None)

backend = LavaSRCompatBackend(
config=InferenceConfig(
device="cuda",
runtime_provider="torch-eager",
model_cache_dir=tmp_path / "models",
)
)
monkeypatch.setattr(backend, "_load_model", lambda *args, **kwargs: pytest.fail("model should not load"))

enhanced = backend.enhance(np.zeros(100, dtype=np.float32), 16000, 48000)

assert enhanced.shape == (300,)
assert np.count_nonzero(enhanced) == 0