Spaces:
Sleeping
Sleeping
| from __future__ import annotations | |
| import hashlib | |
| import json | |
| import os | |
| from dataclasses import asdict, dataclass | |
| from typing import Any | |
| CLAP_MODEL_ID = "laion/clap-htsat-unfused" | |
| CLAP_MODEL_REVISION = "8fa0f1c6d0433df6e97c127f64b2a1d6c0dcda8a" | |
| BGE_MODEL_ID = "BAAI/bge-m3" | |
| BGE_MODEL_REVISION = "5617a9f61b028005a4858fdac845db406aefb181" | |
| def _env_bool(name: str, default: bool) -> bool: | |
| value = os.getenv(name) | |
| if value is None: | |
| return default | |
| return value.strip().lower() in {"1", "true", "yes", "on"} | |
| class GlobalAudioConfig: | |
| sample_rate: int = 48_000 | |
| window_seconds: float = 10.0 | |
| target_windows: int = 4 | |
| coverage_start: float = 0.10 | |
| coverage_end: float = 0.90 | |
| minimum_audio_seconds: float = 1.0 | |
| short_window_padding: str = "repeat_to_window_length" | |
| selection: str = "uniform_over_valid_start_range" | |
| aggregation: str = "l2_each_then_mean_then_l2" | |
| encoder_window_seconds: float = 10.0 | |
| oversize_window_encoding: str = "uniform_10s_subwindows" | |
| encoder_subwindow_aggregation: str = "l2_each_then_mean_then_l2" | |
| decoding_version: str = "ffmpeg-mono-f32le-v1" | |
| resampling: str = "scipy-resample-poly-kaiser5-v1" | |
| feature_extractor: str = "transformers-clap-4.44.2-fixed-input" | |
| inference_batch_size: int = 4 | |
| def __post_init__(self) -> None: | |
| if ( | |
| self.sample_rate <= 0 | |
| or self.window_seconds <= 0 | |
| or self.target_windows <= 0 | |
| or self.inference_batch_size <= 0 | |
| ): | |
| raise ValueError("Global audio rates, window and count must be positive") | |
| if not 0 <= self.coverage_start <= self.coverage_end <= 1: | |
| raise ValueError("Global coverage must satisfy 0 <= start <= end <= 1") | |
| class TemporalAudioConfig: | |
| sample_rate: int = 48_000 | |
| window_seconds: float = 10.0 | |
| hop_seconds: float = 10.0 | |
| max_segments: int = 24 | |
| minimum_audio_seconds: float = 1.0 | |
| short_window_padding: str = "repeat_to_window_length" | |
| segmentation: str = "sliding_with_tail_coverage_and_uniform_limit" | |
| encoder_window_seconds: float = 10.0 | |
| oversize_window_encoding: str = "uniform_10s_subwindows" | |
| encoder_subwindow_aggregation: str = "l2_each_then_mean_then_l2" | |
| decoding_version: str = "ffmpeg-mono-f32le-v1" | |
| resampling: str = "scipy-resample-poly-kaiser5-v1" | |
| feature_extractor: str = "transformers-clap-4.44.2-fixed-input" | |
| inference_batch_size: int = 4 | |
| def __post_init__(self) -> None: | |
| if ( | |
| self.sample_rate <= 0 | |
| or self.window_seconds <= 0 | |
| or self.hop_seconds <= 0 | |
| or self.max_segments <= 0 | |
| or self.inference_batch_size <= 0 | |
| ): | |
| raise ValueError("Temporal audio rates, windows, hop and limit must be positive") | |
| class LyricsConfig: | |
| max_chunk_tokens: int = 512 | |
| batch_size: int = 8 | |
| chunking: str = "section_first_then_token_split" | |
| aggregation: str = "token_weighted_mean_of_l2_chunks_then_l2" | |
| unicode_normalization: str = "NFKC" | |
| pooling: str = "bge-m3-cls" | |
| model_context_tokens: int = 8192 | |
| def __post_init__(self) -> None: | |
| if self.max_chunk_tokens < 4 or self.max_chunk_tokens > self.model_context_tokens: | |
| raise ValueError("Lyrics chunk tokens must be between 4 and model context") | |
| if self.batch_size <= 0: | |
| raise ValueError("Lyrics batch size must be positive") | |
| class LimitConfig: | |
| max_upload_bytes: int = 100 * 1024 * 1024 | |
| max_lyrics_characters: int = 100_000 | |
| max_audio_seconds: float = 30 * 60 | |
| request_timeout_seconds: float = 300.0 | |
| decode_timeout_seconds: float = 120.0 | |
| def __post_init__(self) -> None: | |
| if min( | |
| self.max_upload_bytes, | |
| self.max_lyrics_characters, | |
| self.max_audio_seconds, | |
| self.request_timeout_seconds, | |
| self.decode_timeout_seconds, | |
| ) <= 0: | |
| raise ValueError("All service limits must be positive") | |
| class Settings: | |
| device: str = "auto" | |
| inference_concurrency: int = 1 | |
| ffmpeg_binary: str = "ffmpeg" | |
| ffprobe_binary: str = "ffprobe" | |
| eager_model_loading: bool = False | |
| global_audio: GlobalAudioConfig = GlobalAudioConfig() | |
| temporal_audio: TemporalAudioConfig = TemporalAudioConfig() | |
| lyrics: LyricsConfig = LyricsConfig() | |
| limits: LimitConfig = LimitConfig() | |
| def __post_init__(self) -> None: | |
| if self.inference_concurrency <= 0: | |
| raise ValueError("Inference concurrency must be positive") | |
| if self.global_audio.inference_batch_size != self.temporal_audio.inference_batch_size: | |
| raise ValueError("Global and temporal CLAP analyzers must share a batch size") | |
| def from_env(cls) -> "Settings": | |
| clap_batch_size = int(os.getenv("OPENMUSIC_CLAP_BATCH_SIZE", "4")) | |
| global_audio = GlobalAudioConfig( | |
| window_seconds=float(os.getenv("OPENMUSIC_GLOBAL_WINDOW_SECONDS", "10")), | |
| target_windows=int(os.getenv("OPENMUSIC_GLOBAL_WINDOWS", "4")), | |
| inference_batch_size=clap_batch_size, | |
| ) | |
| temporal_audio = TemporalAudioConfig( | |
| window_seconds=float(os.getenv("OPENMUSIC_TEMPORAL_WINDOW_SECONDS", "10")), | |
| hop_seconds=float(os.getenv("OPENMUSIC_TEMPORAL_HOP_SECONDS", "10")), | |
| max_segments=int(os.getenv("OPENMUSIC_TEMPORAL_MAX_SEGMENTS", "24")), | |
| inference_batch_size=clap_batch_size, | |
| ) | |
| lyrics = LyricsConfig( | |
| max_chunk_tokens=int(os.getenv("OPENMUSIC_LYRICS_CHUNK_TOKENS", "512")), | |
| batch_size=int(os.getenv("OPENMUSIC_LYRICS_BATCH_SIZE", "8")), | |
| ) | |
| limits = LimitConfig( | |
| max_upload_bytes=int(os.getenv("OPENMUSIC_MAX_UPLOAD_BYTES", str(100 * 1024 * 1024))), | |
| max_lyrics_characters=int(os.getenv("OPENMUSIC_MAX_LYRICS_CHARACTERS", "100000")), | |
| max_audio_seconds=float(os.getenv("OPENMUSIC_MAX_AUDIO_SECONDS", "1800")), | |
| request_timeout_seconds=float(os.getenv("OPENMUSIC_REQUEST_TIMEOUT_SECONDS", "300")), | |
| decode_timeout_seconds=float(os.getenv("OPENMUSIC_DECODE_TIMEOUT_SECONDS", "120")), | |
| ) | |
| return cls( | |
| device=os.getenv("OPENMUSIC_DEVICE", "auto"), | |
| inference_concurrency=max(1, int(os.getenv("OPENMUSIC_INFERENCE_CONCURRENCY", "1"))), | |
| ffmpeg_binary=os.getenv("OPENMUSIC_FFMPEG", "ffmpeg"), | |
| ffprobe_binary=os.getenv("OPENMUSIC_FFPROBE", "ffprobe"), | |
| eager_model_loading=_env_bool("OPENMUSIC_EAGER_MODELS", False), | |
| global_audio=global_audio, | |
| temporal_audio=temporal_audio, | |
| lyrics=lyrics, | |
| limits=limits, | |
| ) | |
| def config_dict(config: Any) -> dict[str, Any]: | |
| return asdict(config) | |
| def preprocessing_version(base: str, config: Any) -> str: | |
| """Tie a preprocessing version to every result-affecting configuration value.""" | |
| payload = json.dumps(config_dict(config), sort_keys=True, separators=(",", ":")) | |
| digest = hashlib.sha256(payload.encode("utf-8")).hexdigest()[:12] | |
| return f"{base}.{digest}" | |