Files
vllm-omni/vllm_omni/utils/voice_cache.py
T
+11 f50c5a413f [Qwen3TTS] [TTS] [Feat] Refactor voice cache manager (#2108)
Signed-off-by: JuanPZuluaga <juanz9312@gmal.com>
Signed-off-by: yiliu30 <yi4.liu@intel.com>
Signed-off-by: gcanlin <canlinguosdu@gmail.com>
Signed-off-by: Binh Tang <tangbinhna@gmail.com>
Signed-off-by: Binh Tang <binht@netflix.com>
Signed-off-by: Didan Deng <33117903+wtomin@users.noreply.github.com>
Signed-off-by: Rein Yang <ruiruyang2@gmail.com>
Signed-off-by: CHEN <116010019@link.cuhk.edu.cn>
Signed-off-by: vraiti <vraiti@redhat.com>
Signed-off-by: Songrui625 <songrui625@gmail.com>
Signed-off-by: Lidang Jiang <lidangjiang@gmail.com>
Signed-off-by: Lidang-Jiang <lidangjiang@gmail.com>
Signed-off-by: Alex Brooks <albrooks@redhat.com>
Co-authored-by: JuanPZuluaga <juanz9312@gmal.com>
Co-authored-by: Yi Liu <yi4.liu@intel.com>
Co-authored-by: Hongsheng Liu <liuhongsheng4@huawei.com>
Co-authored-by: Canlin Guo <canlinguosdu@gmail.com>
Co-authored-by: Binh Tang <tangbinhna@gmail.com>
Co-authored-by: Binh Tang <binht@netflix.com>
Co-authored-by: Didan Deng <33117903+wtomin@users.noreply.github.com>
Co-authored-by: rein yang <73573651+R2-Y@users.noreply.github.com>
Co-authored-by: zhumingjue138 <zhumingjue@huawei.com>
Co-authored-by: ChenWenjing <54166744+Shirley125@users.noreply.github.com>
Co-authored-by: vraiti <vraiti@redhat.com>
Co-authored-by: 汪志鹏 <wangzhipeng628@gmail.com>
Co-authored-by: Sy03 <1370724210@qq.com>
Co-authored-by: chickeyton <ngton2014@gmail.com>
Co-authored-by: Jerry Song <46962917+Songrui625@users.noreply.github.com>
Co-authored-by: Lidang Jiang <119769478+Lidang-Jiang@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Alex Brooks <albrooks@redhat.com>
Co-authored-by: linyueqian <linyueqian@outlook.com>
Co-authored-by: Yueqian Lin <70319226+linyueqian@users.noreply.github.com>
2026-04-03 16:51:58 -04:00

90 lines
3.2 KiB
Python

"""In-memory LRU cache for voice extraction artifacts.
Keyed by voice name + extraction mode (e.g. ``"alice:icl"``).
Only named voices are cached; inline ``ref_audio`` without a voice
name is not cached.
Usage::
key = VoiceEmbeddingCache.make_cache_key("alice", xvec_only=False)
cached = cache.get(key)
if cached is None:
# ... extract ...
cache.put(key, {"artifact": result})
"""
import os
import threading
from collections import OrderedDict
from typing import Any
from vllm.logger import init_logger
logger = init_logger(__name__)
_DEFAULT_MAX_ENTRIES = 128
class VoiceEmbeddingCache:
"""LRU cache for voice extraction outputs.
Each entry stores a ``dict[str, Any]`` whose contents are model-specific.
Thread-safe via a lightweight ``threading.Lock``.
"""
def __init__(self, max_entries: int | None = None):
if max_entries is None:
max_entries = int(os.environ.get("VOICE_CACHE_MAX_ENTRIES", _DEFAULT_MAX_ENTRIES))
self._cache: OrderedDict[str, dict[str, Any]] = OrderedDict()
self._max_entries = max_entries
self._lock = threading.Lock()
self._hits = 0
self._misses = 0
logger.info("Voice embedding cache initialized (max_entries=%d)", max_entries)
@staticmethod
def make_cache_key(voice_name: str, xvec_only: bool, created_at: float = 0.0) -> str:
"""Build a cache key from a voice name, upload timestamp, and extraction mode.
Args:
voice_name: The speaker/voice name (case-insensitive, lowered
by the caller).
xvec_only: True for speaker-embedding-only mode, False for
ICL mode (speaker embedding + ref_code).
created_at: Upload timestamp from metadata. Prevents stale cache
hits after a voice is deleted and re-uploaded with the same
name but different audio.
"""
mode = "xvec" if xvec_only else "icl"
return f"{voice_name}:{created_at:.6f}:{mode}"
def get(self, key: str) -> dict[str, Any] | None:
"""Return cached artifacts or ``None`` on miss. Promotes to MRU on hit."""
with self._lock:
if key in self._cache:
self._cache.move_to_end(key)
self._hits += 1
logger.debug("Voice cache HIT (key=%s, hits=%d)", key, self._hits)
return self._cache[key]
self._misses += 1
return None
def put(self, key: str, artifacts: dict[str, Any]) -> None:
"""Store *artifacts* under *key*, evicting the LRU entry if full."""
with self._lock:
self._cache[key] = artifacts
self._cache.move_to_end(key)
while len(self._cache) > self._max_entries:
evicted_key, _ = self._cache.popitem(last=False)
logger.debug("Voice cache EVICT (key=%s)", evicted_key)
def stats(self) -> dict[str, int]:
"""Return cache statistics."""
with self._lock:
return {
"entries": len(self._cache),
"max_entries": self._max_entries,
"hits": self._hits,
"misses": self._misses,
}