mirror of
https://github.com/JefferyHcool/BiliNote.git
synced 2026-09-05 23:56:59 +08:00
Merge pull request #425 from pumpkinperson996/agent/fix-whisper-model-selection
Fix Whisper model selection caching
This commit is contained in:
@@ -262,7 +262,10 @@ class NoteGenerator:
|
|||||||
raise Exception(f"不支持的转写器:{self.transcriber_type}")
|
raise Exception(f"不支持的转写器:{self.transcriber_type}")
|
||||||
|
|
||||||
logger.info(f"使用转写器:{self.transcriber_type}")
|
logger.info(f"使用转写器:{self.transcriber_type}")
|
||||||
return get_transcriber(transcriber_type=self.transcriber_type)
|
return get_transcriber(
|
||||||
|
transcriber_type=self.transcriber_type,
|
||||||
|
model_size=self.model_size,
|
||||||
|
)
|
||||||
|
|
||||||
def _get_gpt(self, model_name: Optional[str], provider_id: Optional[str]) -> GPT:
|
def _get_gpt(self, model_name: Optional[str], provider_id: Optional[str]) -> GPT:
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
|
import threading
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
from app.transcriber.groq import GroqTranscriber
|
from app.transcriber.groq import GroqTranscriber
|
||||||
@@ -38,17 +39,29 @@ _transcribers = {
|
|||||||
TranscriberType.GROQ: None,
|
TranscriberType.GROQ: None,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Cache instances together with their constructor configuration. The
|
||||||
|
# transcriber choice and Whisper model size can be changed from the frontend,
|
||||||
|
# so caching by transcriber type alone would keep using the first loaded model.
|
||||||
|
_transcriber_configs = {key: None for key in _transcribers}
|
||||||
|
_transcriber_init_lock = threading.Lock()
|
||||||
|
|
||||||
# 公共实例初始化函数
|
# 公共实例初始化函数
|
||||||
def _init_transcriber(key: TranscriberType, cls, *args, **kwargs):
|
def _init_transcriber(key: TranscriberType, cls, *args, **kwargs):
|
||||||
if _transcribers[key] is None:
|
init_config = (args, tuple(sorted(kwargs.items())))
|
||||||
logger.info(f'创建 {cls.__name__} 实例: {key}')
|
with _transcriber_init_lock:
|
||||||
try:
|
instance = _transcribers[key]
|
||||||
_transcribers[key] = cls(*args, **kwargs)
|
if instance is None or _transcriber_configs[key] != init_config:
|
||||||
|
action = "创建" if instance is None else "按新配置重新创建"
|
||||||
|
logger.info(f'{action} {cls.__name__} 实例: {key}')
|
||||||
|
try:
|
||||||
|
new_instance = cls(*args, **kwargs)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"{cls.__name__} 创建失败: {e}")
|
||||||
|
raise
|
||||||
|
_transcribers[key] = new_instance
|
||||||
|
_transcriber_configs[key] = init_config
|
||||||
logger.info(f'{cls.__name__} 创建成功')
|
logger.info(f'{cls.__name__} 创建成功')
|
||||||
except Exception as e:
|
return _transcribers[key]
|
||||||
logger.error(f"{cls.__name__} 创建失败: {e}")
|
|
||||||
raise
|
|
||||||
return _transcribers[key]
|
|
||||||
|
|
||||||
# 各类型获取方法
|
# 各类型获取方法
|
||||||
def get_groq_transcriber():
|
def get_groq_transcriber():
|
||||||
@@ -70,13 +83,13 @@ def get_mlx_whisper_transcriber(model_size="base"):
|
|||||||
return _init_transcriber(TranscriberType.MLX_WHISPER, MLXWhisperTranscriber, model_size=model_size)
|
return _init_transcriber(TranscriberType.MLX_WHISPER, MLXWhisperTranscriber, model_size=model_size)
|
||||||
|
|
||||||
# 通用入口
|
# 通用入口
|
||||||
def get_transcriber(transcriber_type="fast-whisper", model_size="base", device="cuda"):
|
def get_transcriber(transcriber_type="fast-whisper", model_size=None, device="cuda"):
|
||||||
"""
|
"""
|
||||||
获取指定类型的转录器实例
|
获取指定类型的转录器实例
|
||||||
|
|
||||||
参数:
|
参数:
|
||||||
transcriber_type: 支持 "fast-whisper", "mlx-whisper", "bcut", "kuaishou", "groq"
|
transcriber_type: 支持 "fast-whisper", "mlx-whisper", "bcut", "kuaishou", "groq"
|
||||||
model_size: 模型大小,适用于 whisper 类
|
model_size: 模型大小,适用于 whisper 类;未提供时才读取环境变量默认值
|
||||||
device: 设备类型(如 cuda / cpu),仅 whisper 使用
|
device: 设备类型(如 cuda / cpu),仅 whisper 使用
|
||||||
|
|
||||||
返回:
|
返回:
|
||||||
@@ -90,7 +103,9 @@ def get_transcriber(transcriber_type="fast-whisper", model_size="base", device="
|
|||||||
logger.warning(f'未知转录器类型 "{transcriber_type}",默认使用 fast-whisper')
|
logger.warning(f'未知转录器类型 "{transcriber_type}",默认使用 fast-whisper')
|
||||||
transcriber_enum = TranscriberType.FAST_WHISPER
|
transcriber_enum = TranscriberType.FAST_WHISPER
|
||||||
|
|
||||||
whisper_model_size = os.environ.get("WHISPER_MODEL_SIZE", model_size)
|
# The explicit value normally comes from the persisted frontend setting and
|
||||||
|
# must take precedence over Docker's startup default.
|
||||||
|
whisper_model_size = model_size or os.environ.get("WHISPER_MODEL_SIZE", "base")
|
||||||
|
|
||||||
if transcriber_enum == TranscriberType.FAST_WHISPER:
|
if transcriber_enum == TranscriberType.FAST_WHISPER:
|
||||||
return get_whisper_transcriber(whisper_model_size, device=device)
|
return get_whisper_transcriber(whisper_model_size, device=device)
|
||||||
|
|||||||
@@ -0,0 +1,120 @@
|
|||||||
|
import os
|
||||||
|
import pathlib
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import textwrap
|
||||||
|
|
||||||
|
|
||||||
|
ROOT = pathlib.Path(__file__).resolve().parents[1]
|
||||||
|
|
||||||
|
|
||||||
|
def _run_isolated(script: str) -> None:
|
||||||
|
env = os.environ.copy()
|
||||||
|
env["PYTHONPATH"] = os.pathsep.join(
|
||||||
|
filter(None, [str(ROOT), env.get("PYTHONPATH")])
|
||||||
|
)
|
||||||
|
result = subprocess.run(
|
||||||
|
[sys.executable, "-c", textwrap.dedent(script)],
|
||||||
|
cwd=ROOT,
|
||||||
|
env=env,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
)
|
||||||
|
assert result.returncode == 0, result.stdout + result.stderr
|
||||||
|
|
||||||
|
|
||||||
|
def test_note_generator_forwards_configured_whisper_model_size():
|
||||||
|
_run_isolated(
|
||||||
|
"""
|
||||||
|
from app.services import note as note_service
|
||||||
|
|
||||||
|
calls = []
|
||||||
|
|
||||||
|
def fake_get_transcriber(**kwargs):
|
||||||
|
calls.append(kwargs)
|
||||||
|
return object()
|
||||||
|
|
||||||
|
note_service.get_transcriber = fake_get_transcriber
|
||||||
|
|
||||||
|
generator = note_service.NoteGenerator.__new__(note_service.NoteGenerator)
|
||||||
|
generator.transcriber_type = "fast-whisper"
|
||||||
|
generator.model_size = "large-v3-turbo"
|
||||||
|
|
||||||
|
generator._init_transcriber()
|
||||||
|
|
||||||
|
assert calls == [
|
||||||
|
{
|
||||||
|
"transcriber_type": "fast-whisper",
|
||||||
|
"model_size": "large-v3-turbo",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_whisper_cache_is_rebuilt_when_model_size_changes():
|
||||||
|
_run_isolated(
|
||||||
|
"""
|
||||||
|
from app.transcriber import transcriber_provider as provider
|
||||||
|
|
||||||
|
class FakeWhisperTranscriber:
|
||||||
|
def __init__(self, model_size, device):
|
||||||
|
self.model_size = model_size
|
||||||
|
self.device = device
|
||||||
|
|
||||||
|
provider._transcribers = {key: None for key in provider._transcribers}
|
||||||
|
provider._transcriber_configs = {
|
||||||
|
key: None for key in provider._transcribers
|
||||||
|
}
|
||||||
|
provider.WhisperTranscriber = FakeWhisperTranscriber
|
||||||
|
|
||||||
|
base = provider.get_whisper_transcriber("base", device="cpu")
|
||||||
|
turbo = provider.get_whisper_transcriber("large-v3-turbo", device="cpu")
|
||||||
|
turbo_again = provider.get_whisper_transcriber(
|
||||||
|
"large-v3-turbo", device="cpu"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert base.model_size == "base"
|
||||||
|
assert turbo.model_size == "large-v3-turbo"
|
||||||
|
assert turbo is not base
|
||||||
|
assert turbo_again is turbo
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_explicit_model_size_wins_over_environment_default():
|
||||||
|
_run_isolated(
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
|
||||||
|
from app.transcriber import transcriber_provider as provider
|
||||||
|
|
||||||
|
class FakeWhisperTranscriber:
|
||||||
|
def __init__(self, model_size, device):
|
||||||
|
self.model_size = model_size
|
||||||
|
self.device = device
|
||||||
|
|
||||||
|
os.environ["WHISPER_MODEL_SIZE"] = "tiny"
|
||||||
|
provider._transcribers = {key: None for key in provider._transcribers}
|
||||||
|
provider._transcriber_configs = {
|
||||||
|
key: None for key in provider._transcribers
|
||||||
|
}
|
||||||
|
provider.WhisperTranscriber = FakeWhisperTranscriber
|
||||||
|
|
||||||
|
transcriber = provider.get_transcriber(
|
||||||
|
transcriber_type="fast-whisper",
|
||||||
|
model_size="large-v3-turbo",
|
||||||
|
device="cpu",
|
||||||
|
)
|
||||||
|
|
||||||
|
assert transcriber.model_size == "large-v3-turbo"
|
||||||
|
|
||||||
|
fallback = provider.get_transcriber(
|
||||||
|
transcriber_type="fast-whisper",
|
||||||
|
model_size=None,
|
||||||
|
device="cpu",
|
||||||
|
)
|
||||||
|
|
||||||
|
assert fallback.model_size == "tiny"
|
||||||
|
"""
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user