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
49 changes: 38 additions & 11 deletions music_assistant/helpers/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,23 +157,50 @@ def verify_system_meets_requirements(
torch inference (AVX2 on x86). Checked last, as it imports torch.
:raises UnsupportedSystemError: If the system does not meet the requirements.
"""
cpu_cores = os.process_cpu_count() or os.cpu_count() or 1
if min_cpu_cores and cpu_cores < min_cpu_cores:
if shortfall := _resource_shortfall(min_memory_gb=min_memory_gb, min_cpu_cores=min_cpu_cores):
raise UnsupportedSystemError(
f"This system does not meet the minimal requirements for {feature_name}: "
f"at least {min_cpu_cores} CPU cores are required ({cpu_cores} detected)."
f"This system does not meet the minimal requirements for {feature_name}: {shortfall}"
)
if require_ml_inference:
verify_cpu_supports_ml_inference()


def system_meets_requirements(
*,
min_memory_gb: float = 0.0,
min_cpu_cores: int = 0,
) -> bool:
"""
Return whether the host meets the given RAM/CPU thresholds.

A non-raising companion to verify_system_meets_requirements for soft UI hints
(e.g. hiding a recommended-hardware notice) rather than gating setup. The
ML-inference capability is not considered here.

:param min_memory_gb: Minimum total system RAM in GB (0 disables the check).
:param min_cpu_cores: Minimum CPU core count (0 disables the check).
"""
return _resource_shortfall(min_memory_gb=min_memory_gb, min_cpu_cores=min_cpu_cores) is None


def _resource_shortfall(*, min_memory_gb: float, min_cpu_cores: int) -> str | None:
"""
Return why the host falls short of the RAM/CPU thresholds, or None if it meets them.

:param min_memory_gb: Minimum total system RAM in GB (0 disables the check).
:param min_cpu_cores: Minimum CPU core count (0 disables the check).
"""
cpu_cores = os.process_cpu_count() or os.cpu_count() or 1
if min_cpu_cores and cpu_cores < min_cpu_cores:
return f"at least {min_cpu_cores} CPU cores are required ({cpu_cores} detected)."
total_memory_gb = get_total_system_memory()
# get_total_system_memory() returns 0.0 when the platform cannot report memory
# (e.g. Windows); treat that as unknown and fail open rather than block setup.
# (e.g. Windows); treat that as unknown and pass rather than block on a guess.
if min_memory_gb and total_memory_gb and total_memory_gb < min_memory_gb:
raise UnsupportedSystemError(
f"This system does not meet the minimal requirements for {feature_name}: "
f"at least {min_memory_gb:.0f}GB of RAM is required "
f"({total_memory_gb:.1f}GB detected)."
return (
f"at least {min_memory_gb:.0f}GB of RAM is required ({total_memory_gb:.1f}GB detected)."
)
if require_ml_inference:
verify_cpu_supports_ml_inference()
return None


def verify_cpu_supports_ml_inference() -> None:
Expand Down
19 changes: 14 additions & 5 deletions music_assistant/providers/smart_fades/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@

from __future__ import annotations

import os
from typing import TYPE_CHECKING

from music_assistant_models.config_entries import ConfigEntry, ConfigValueType
from music_assistant_models.enums import ConfigEntryType

from music_assistant.helpers.util import verify_system_meets_requirements
from music_assistant.helpers.util import (
system_meets_requirements,
verify_system_meets_requirements,
)

if TYPE_CHECKING:
from music_assistant_models.config_entries import ProviderConfig
Expand All @@ -24,7 +26,11 @@
# Smart Fades runs on-device ML (torch) inference; gate it to capable hardware.
# 4GB matches the Balanced buffer threshold, the minimum buffer smart crossfade needs.
MIN_RAM_GB = 4.0
MIN_CPU_CORES = 4
MIN_CPU_CORES = 2
# Below the recommended thresholds the provider still runs, but we surface an
# informational notice (see get_config_entries) as it may be tight under load.
RECOMMENDED_RAM_GB = 6.0
RECOMMENDED_CPU_CORES = 4


async def setup(
Expand Down Expand Up @@ -56,9 +62,12 @@ async def get_config_entries(
# ruff: noqa: ARG001
return (
ConfigEntry(
key="single_cpu_warning",
key="resource_warning",
type=ConfigEntryType.ALERT,
required=False,
hidden=(os.cpu_count() or 1) > 1,
hidden=system_meets_requirements(
min_memory_gb=RECOMMENDED_RAM_GB,
min_cpu_cores=RECOMMENDED_CPU_CORES,
),
),
)
4 changes: 2 additions & 2 deletions music_assistant/providers/smart_fades/strings.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"config_entries": {
"single_cpu_warning": {
"label": "Only 1 CPU core detected on this system. Smart Fades analysis typically takes some CPU time during model inference. Enabling it on single-CPU hosts may cause performance issues and block normal playback. Enable at your own risk."
"resource_warning": {
"label": "Smart Fades is powerful but resource-intensive: it loads a machine-learning model into memory to analyze your music. The minimum requirements are 4 GB of RAM, 2 CPU cores and (on Intel/AMD CPUs) AVX2 support. For the best experience we recommend 6 GB or more of RAM and 4 CPU cores. On systems near the minimum, or under heavy load, you may still run into out-of-memory situations."
}
},
"manifest": {
Expand Down
22 changes: 19 additions & 3 deletions music_assistant/providers/sonic_analysis/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@
from music_assistant_models.config_entries import ConfigEntry, ConfigValueOption
from music_assistant_models.enums import ConfigEntryType, ContentType

from music_assistant.helpers.util import verify_system_meets_requirements
from music_assistant.helpers.util import (
system_meets_requirements,
verify_system_meets_requirements,
)
from music_assistant.models.audio_analysis import AudioAnalysisData
from music_assistant.models.audio_analysis_provider import (
AnalysisSessionData,
Expand Down Expand Up @@ -67,8 +70,12 @@
CONF_CLAP_SAMPLING: str = "clap_sampling"

# Sonic Analysis runs on-device CLAP inference; gate it to capable hardware.
MIN_RAM_GB: float = 8.0
MIN_CPU_CORES: int = 4
MIN_RAM_GB: float = 4.0
MIN_CPU_CORES: int = 2
# Below the recommended thresholds the provider still runs, but we surface an
# informational notice (see get_config_entries) as it may be tight under load.
RECOMMENDED_RAM_GB: float = 6.0
RECOMMENDED_CPU_CORES: int = 4


@dataclass
Expand Down Expand Up @@ -118,6 +125,15 @@ async def get_config_entries(
:param values: the (intermediate) raw values for config entries sent with the action.
"""
return (
ConfigEntry(
key="resource_warning",
type=ConfigEntryType.ALERT,
required=False,
hidden=system_meets_requirements(
min_memory_gb=RECOMMENDED_RAM_GB,
min_cpu_cores=RECOMMENDED_CPU_CORES,
),
),
ConfigEntry(
key=CONF_CLAP_SAMPLING,
type=ConfigEntryType.STRING,
Expand Down
3 changes: 3 additions & 0 deletions music_assistant/providers/sonic_analysis/strings.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
{
"config_entries": {
"resource_warning": {
"label": "Sonic Analysis is powerful but resource-intensive: it loads a machine-learning model (CLAP) into memory to analyze how your music sounds. The minimum requirements are 4 GB of RAM, 2 CPU cores and (on Intel/AMD CPUs) AVX2 support. For the best experience we recommend 6 GB or more of RAM and 4 CPU cores. On systems near the minimum, or under heavy load, you may still run into out-of-memory situations."
},
"clap_sampling": {
"label": "CLAP quality (windows per track)",
"description": "Number of 7-second windows CLAP analyzes per track. More windows produce more representative scalars at linear CPU cost. Thorough is most useful for instrumentalness, where vocals can be missed by a single window.",
Expand Down
3 changes: 2 additions & 1 deletion music_assistant/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1153,7 +1153,7 @@
"provider.siriusxm.config_entries.sxm_region.options.CA": "Canada",
"provider.siriusxm.config_entries.sxm_region.options.US": "United States",
"provider.siriusxm.manifest.description": "Stream SiriusXM’s radio, talk, and curated music channels.",
"provider.smart_fades.config_entries.single_cpu_warning.label": "Only 1 CPU core detected on this system. Smart Fades analysis typically takes some CPU time during model inference. Enabling it on single-CPU hosts may cause performance issues and block normal playback. Enable at your own risk.",
"provider.smart_fades.config_entries.resource_warning.label": "Smart Fades is powerful but resource-intensive: it loads a machine-learning model into memory to analyze your music. The minimum requirements are 4 GB of RAM, 2 CPU cores and (on Intel/AMD CPUs) AVX2 support. For the best experience we recommend 6 GB or more of RAM and 4 CPU cores. On systems near the minimum, or under heavy load, you may still run into out-of-memory situations.",
"provider.smart_fades.manifest.description": "Smart fades analyzes beat and downbeat detection, energy and musical key for smart crossfades.",
"provider.smart_playlist.config_entries.ai_descriptions.description": "When a provider with AI support is available, use it to write a natural-language description for each smart playlist. Falls back to a plain rules summary when no AI provider is available.",
"provider.smart_playlist.config_entries.ai_descriptions.label": "Generate descriptions with AI",
Expand Down Expand Up @@ -1182,6 +1182,7 @@
"provider.sonic_analysis.config_entries.clap_sampling.options.balanced": "Balanced (3 windows, 2.4x CPU)",
"provider.sonic_analysis.config_entries.clap_sampling.options.fast": "Fast (1 window)",
"provider.sonic_analysis.config_entries.clap_sampling.options.thorough": "Thorough (8 windows, 6.6x CPU)",
"provider.sonic_analysis.config_entries.resource_warning.label": "Sonic Analysis is powerful but resource-intensive: it loads a machine-learning model (CLAP) into memory to analyze how your music sounds. The minimum requirements are 4 GB of RAM, 2 CPU cores and (on Intel/AMD CPUs) AVX2 support. For the best experience we recommend 6 GB or more of RAM and 4 CPU cores. On systems near the minimum, or under heavy load, you may still run into out-of-memory situations.",
"provider.sonic_analysis.manifest.description": "Analyses how each track sounds to power similarity, mood-based playlists, and other audio-aware features.",
"provider.sonic_similarity.config_entries.discover_diversity.description": "0 keeps results closest to the seeds; 10 maximises variety via MMR (some results may be less similar but more distinct from each other). Traits engine only.",
"provider.sonic_similarity.config_entries.discover_diversity.label": "Discover row diversity",
Expand Down
19 changes: 19 additions & 0 deletions tests/core/test_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -504,3 +504,22 @@ def test_verify_system_meets_requirements_ml_inference() -> None:
feature_name="X", min_cpu_cores=4, min_memory_gb=8.0, require_ml_inference=True
)
util.verify_system_meets_requirements(feature_name="X", min_cpu_cores=4, min_memory_gb=8.0)


@pytest.mark.parametrize(
("cpu_cores", "total_gb", "expected"),
[
(4, 6.0, True), # meets both recommended thresholds
(8, 16.0, True),
(2, 6.0, False), # below recommended cores
(4, 4.0, False), # below recommended RAM
(4, 0.0, True), # unknown memory -> fail open, same as the gate
],
)
def test_system_meets_requirements(cpu_cores: int, total_gb: float, expected: bool) -> None:
"""The non-raising predicate mirrors the gate's RAM/CPU checks, failing open on unknown RAM."""
with (
patch("music_assistant.helpers.util.os.process_cpu_count", return_value=cpu_cores),
patch("music_assistant.helpers.util.get_total_system_memory", return_value=total_gb),
):
assert util.system_meets_requirements(min_memory_gb=6.0, min_cpu_cores=4) is expected