Skip to content
Open
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
38 changes: 38 additions & 0 deletions .github/workflows/codspeed.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: CodSpeed

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
# `workflow_dispatch` allows CodSpeed to trigger backtest
# performance analysis in order to generate initial data.
workflow_dispatch:

permissions:
contents: read
id-token: write # for OpenID Connect authentication with CodSpeed

jobs:
benchmarks:
name: Run benchmarks
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r test/benchmarks/requirements.txt

- name: Run benchmarks
uses: CodSpeedHQ/action@v4

Check failure on line 35 in .github/workflows/codspeed.yml

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use full commit SHA hash for this dependency.

See more on https://sonarcloud.io/project/issues?id=NeptuneHub_AudioMuse-AI&issues=AZ9Y14QWJgY_rLCyJSee&open=AZ9Y14QWJgY_rLCyJSee&pullRequest=753
with:
mode: simulation
run: pytest test/benchmarks/ --codspeed
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
![Latest Tag](https://img.shields.io/github/v/tag/neptunehub/AudioMuse-AI?label=latest-tag)
![Media Server Support: Navidrome 0.62.0, Jellyfin 12.0, LMS v3.69.0, Lyrion 9.0.2, Emby 4.9.1.80, Plex 1.43.2](https://img.shields.io/badge/Media%20Server-Navidrome%200.62.0%2C%20Jellyfin%2012.0%2C%20LMS%20v3.69.0%2C%20Lyrion%209.0.2%2C%20Emby%204.9.1.80%2C%20Plex%201.43.2-blue?style=flat-square&logo=server&logoColor=white)
<a href="https://www.bestpractices.dev/projects/13329"><img src="https://www.bestpractices.dev/projects/13329/badge"></a>
[![CodSpeed](https://img.shields.io/endpoint?url=https://codspeed.io/badge.json)](https://app.codspeed.io/NeptuneHub/AudioMuse-AI?utm_source=badge)

<p align="center">
<strong>⭐ Leave a star on this project:</strong> One shines alone; together, they make it visible and keep it alive.
Expand Down
9 changes: 9 additions & 0 deletions test/benchmarks/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# AudioMuse-AI - https://github.com/NeptuneHub/AudioMuse-AI
# Copyright (C) 2025 NeptuneHub
# SPDX-License-Identifier: AGPL-3.0-only
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License v3.0. See the LICENSE file
# in the project root or <https://github.com/NeptuneHub/AudioMuse-AI/blob/main/LICENSE>

"""Package marker for the AudioMuse-AI CodSpeed performance benchmarks."""
8 changes: 8 additions & 0 deletions test/benchmarks/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Dependencies for running the CodSpeed performance benchmarks in test/benchmarks/.
# These benchmarks target self-contained, dependency-light hot paths, so they
# only need numpy (for the sanitization helpers) plus the CodSpeed pytest plugin.
# numpy is pinned to match test/requirements.txt and requirements/common.txt.

numpy==1.26.4
pytest>=7.0.0
pytest-codspeed
75 changes: 75 additions & 0 deletions test/benchmarks/test_bench_playlist_ordering.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# AudioMuse-AI - https://github.com/NeptuneHub/AudioMuse-AI
# Copyright (C) 2025 NeptuneHub
# SPDX-License-Identifier: AGPL-3.0-only
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License v3.0. See the LICENSE file
# in the project root or <https://github.com/NeptuneHub/AudioMuse-AI/blob/main/LICENSE>

"""CodSpeed performance benchmarks for the playlist sonic-ordering distances.

Measures the pure distance helpers that drive the greedy nearest-neighbour walk
used when sequencing playlists. These run once per candidate pair, so their cost
scales quadratically with playlist size and is worth tracking.

Main Features:
* Benchmarks the circle-of-fifths key distance across many key/scale pairs.
* Benchmarks the composite tempo/energy/key distance over a realistic song set.
"""

import pytest

from test.unit.conftest import _import_module


playlist_ordering = _import_module(
'tasks.playlist_ordering', 'tasks/playlist_ordering.py'
)

KEYS = ['C', 'G', 'D', 'A', 'E', 'B', 'F#', 'Db', 'Ab', 'Eb', 'Bb', 'F', None, 'XYZ']
SCALES = ['major', 'minor', None]

KEY_PAIRS = [
(k1, s1, k2, s2)
for k1 in KEYS
for s1 in SCALES
for k2 in KEYS
for s2 in SCALES
]

SONGS = [
{
'tempo': 60 + (i * 7) % 140,
'energy': (i % 20) / 20.0,
'key': KEYS[i % len(KEYS)],
'scale': SCALES[i % len(SCALES)],
}
for i in range(200)
]


def _run_key_distances():
total = 0.0
for k1, s1, k2, s2 in KEY_PAIRS:
total += playlist_ordering._key_distance(k1, s1, k2, s2)
return total


def _run_composite_distances():
total = 0.0
for a in SONGS:
for b in SONGS:
total += playlist_ordering._composite_distance(a, b)
return total


@pytest.mark.benchmark
def test_bench_key_distance(benchmark):
result = benchmark(_run_key_distances)
assert result >= 0.0


@pytest.mark.benchmark
def test_bench_composite_distance(benchmark):
result = benchmark(_run_composite_distances)
assert result >= 0.0
75 changes: 75 additions & 0 deletions test/benchmarks/test_bench_sanitization.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# AudioMuse-AI - https://github.com/NeptuneHub/AudioMuse-AI
# Copyright (C) 2025 NeptuneHub
# SPDX-License-Identifier: AGPL-3.0-only
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License v3.0. See the LICENSE file
# in the project root or <https://github.com/NeptuneHub/AudioMuse-AI/blob/main/LICENSE>

"""CodSpeed performance benchmarks for the sanitization helpers.

Measures the string and JSON sanitization paths that run on every database
write and API response, using realistic payload sizes so the benchmarks track
the cost of the regex stripping and numpy conversion hot paths.

Main Features:
* Benchmarks NUL/control-character stripping on plain strings.
* Benchmarks nested-JSON sanitization and numpy-to-native conversion.
"""

import numpy as np
import pytest

import sanitization


DIRTY_STRING = (
"Song\x00Title \x01with \x02control\x1f chars and a long tail "
"of unicode text \u00e9\u00e8\u00ea " * 40
)

NESTED_JSON = {
"tracks": [
{
"item_id": f"track-{i}",
"title": f"Title\x00 {i}",
"author": f"Artist\x1f {i}",
"moods": {"happy": 0.8, "energetic": 0.6, "calm": 0.2},
"tags": [f"tag\x00{j}" for j in range(5)],
}
for i in range(50)
],
"meta": {"note": "clean\x00note", "count": 50},
}

NUMPY_PAYLOAD = {
"embedding": np.random.rand(200),

Check warning on line 46 in test/benchmarks/test_bench_sanitization.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use a "numpy.random.Generator" here instead of this legacy function.

See more on https://sonarcloud.io/project/issues?id=NeptuneHub_AudioMuse-AI&issues=AZ9Y14MaJgY_rLCyJSeb&open=AZ9Y14MaJgY_rLCyJSeb&pullRequest=753
"scores": [np.float64(v) for v in np.random.rand(50)],

Check warning on line 47 in test/benchmarks/test_bench_sanitization.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use a "numpy.random.Generator" here instead of this legacy function.

See more on https://sonarcloud.io/project/issues?id=NeptuneHub_AudioMuse-AI&issues=AZ9Y14MaJgY_rLCyJSec&open=AZ9Y14MaJgY_rLCyJSec&pullRequest=753
"counts": {"a": np.int64(3), "b": np.int32(7)},
"flag": np.bool_(True),
"matrix": np.random.rand(20, 20),

Check warning on line 50 in test/benchmarks/test_bench_sanitization.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use a "numpy.random.Generator" here instead of this legacy function.

See more on https://sonarcloud.io/project/issues?id=NeptuneHub_AudioMuse-AI&issues=AZ9Y14MaJgY_rLCyJSed&open=AZ9Y14MaJgY_rLCyJSed&pullRequest=753
}


@pytest.mark.benchmark
def test_bench_sanitize_string_for_db(benchmark):
result = benchmark(sanitization.sanitize_string_for_db, DIRTY_STRING)
assert "\x00" not in result


@pytest.mark.benchmark
def test_bench_sanitize_db_field(benchmark):
result = benchmark(sanitization.sanitize_db_field, DIRTY_STRING, 1000, "title")
assert "\x00" not in result


@pytest.mark.benchmark
def test_bench_sanitize_json_for_db(benchmark):
result = benchmark(sanitization.sanitize_json_for_db, NESTED_JSON)
assert result["meta"]["count"] == 50


@pytest.mark.benchmark
def test_bench_sanitize_for_json(benchmark):
result = benchmark(sanitization.sanitize_for_json, NUMPY_PAYLOAD)
assert isinstance(result["embedding"], list)
Loading