Release 2.3.7 Whisper offline cache fix
This commit is contained in:
+34
-2
@@ -160,6 +160,19 @@ def _missing_audio_result(notes: list[str] | None = None) -> tuple[str, str, str
|
||||
return message, "[]", "\n".join(trace_lines)
|
||||
|
||||
|
||||
def _is_local_whisper_cache_miss(exc: Exception) -> bool:
|
||||
try:
|
||||
from huggingface_hub.errors import LocalEntryNotFoundError
|
||||
|
||||
if isinstance(exc, LocalEntryNotFoundError):
|
||||
return True
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
message = str(exc or "").lower()
|
||||
return "local_files_only" in message and "cached" in message
|
||||
|
||||
|
||||
class ThinkingLLM_Whisper_ASR:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
@@ -219,7 +232,25 @@ class ThinkingLLM_Whisper_ASR:
|
||||
|
||||
resolved_device = _resolve_whisper_device(str(device))
|
||||
resolved_compute_type = _resolve_compute_type(resolved_device, str(compute_type))
|
||||
model = WhisperModel(str(model_size), device=resolved_device, compute_type=resolved_compute_type)
|
||||
local_files_only = True
|
||||
try:
|
||||
model = WhisperModel(
|
||||
str(model_size),
|
||||
device=resolved_device,
|
||||
compute_type=resolved_compute_type,
|
||||
local_files_only=True,
|
||||
)
|
||||
except Exception as exc:
|
||||
if not _is_local_whisper_cache_miss(exc):
|
||||
raise
|
||||
notes.append("Local Whisper cache miss; downloaded model from Hugging Face once.")
|
||||
local_files_only = False
|
||||
model = WhisperModel(
|
||||
str(model_size),
|
||||
device=resolved_device,
|
||||
compute_type=resolved_compute_type,
|
||||
local_files_only=False,
|
||||
)
|
||||
segments_iter, info = model.transcribe(
|
||||
str(wav_path),
|
||||
language=None if language == "auto" else str(language),
|
||||
@@ -245,9 +276,10 @@ class ThinkingLLM_Whisper_ASR:
|
||||
[
|
||||
"[WHISPER]",
|
||||
f"source={source_label}",
|
||||
f"model_size={model_size}; device={resolved_device}; compute_type={resolved_compute_type}; task={task}; vad_filter={bool(vad_filter)}",
|
||||
f"model_size={model_size}; device={resolved_device}; compute_type={resolved_compute_type}; local_files_only={local_files_only}; task={task}; vad_filter={bool(vad_filter)}",
|
||||
f"language={detected_language}; language_probability={detected_probability}; duration={duration}",
|
||||
f"segments={len(segments)}; transcript_chars={len(transcript)}",
|
||||
*notes,
|
||||
]
|
||||
)
|
||||
return transcript, segments_json, trace
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
"name": "ComfyUI-ThinkingLLM",
|
||||
"description": "A multimodal ComfyUI AI node with Qwen3.5, Qwen3-VL, Qwen2.5-VL, Qwen3, and Gemma 4 integrations. Features live thinking in the terminal to see what the LLM is doing in real time.",
|
||||
"author": "goodguy1963",
|
||||
"version": "2.3.6",
|
||||
"version": "2.3.7",
|
||||
"url": "https://github.com/goodguy1963/ComfyUI-ThinkingLLM",
|
||||
"category": "image"
|
||||
}
|
||||
|
||||
+1
-1
@@ -9,5 +9,5 @@
|
||||
"description": "A multimodal ComfyUI AI node with Qwen3.5, Qwen3-VL, Qwen2.5-VL, Qwen3, and Gemma 4 integrations. Features live thinking in the terminal to see what the LLM is doing in real time.",
|
||||
"category": "image",
|
||||
"tags": ["vision", "language", "multimodal", "thinkingllm", "qwen", "gguf", "flash_attention", "performance", "wan2.2", "video", "i2v", "t2v", "storyboard", "caching", "fork", "gpl-3.0"],
|
||||
"version": "2.3.6"
|
||||
"version": "2.3.7"
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
[project]
|
||||
name = "ComfyUI-ThinkingLLM"
|
||||
description = "A multimodal ComfyUI AI node with Qwen3.5, Qwen3-VL, Qwen2.5-VL, Qwen3, and Gemma 4 integrations. Features live thinking in the terminal to see what the LLM is doing in real time."
|
||||
version = "2.3.6"
|
||||
version = "2.3.7"
|
||||
license = {file = "LICENSE"}
|
||||
dependencies = ["transformers>=5.2.0", "torch", "huggingface-hub", "hf_xet", "psutil", "numpy", "Pillow", "opencv-python", "bitsandbytes", "accelerate", "sentencepiece", "tiktoken", "faster-whisper", "llama-cpp-python"]
|
||||
|
||||
|
||||
@@ -1049,7 +1049,7 @@ class TestGGUFAdvancedWorkflowCompatibility(unittest.TestCase):
|
||||
captured = {}
|
||||
|
||||
class FakeWhisperModel:
|
||||
def __init__(self, model_size, device, compute_type):
|
||||
def __init__(self, model_size, device, compute_type, local_files_only=False):
|
||||
captured["init"] = (model_size, device, compute_type)
|
||||
|
||||
def transcribe(self, path, language, task, beam_size, vad_filter):
|
||||
@@ -1083,6 +1083,101 @@ class TestGGUFAdvancedWorkflowCompatibility(unittest.TestCase):
|
||||
self.assertEqual(captured["transcribe"], (".wav", "de", "transcribe", 3, True))
|
||||
self.assertIn("language=de", raw_trace)
|
||||
|
||||
def test_whisper_asr_prefers_cached_model_without_hf_ping(self):
|
||||
module = load_module_from_file(
|
||||
"AILab_WhisperASR.py",
|
||||
"thinkingllm_whisper_cached_model_test",
|
||||
)
|
||||
|
||||
class FakeSegment:
|
||||
start = 0.0
|
||||
end = 1.0
|
||||
text = " cached transcript "
|
||||
|
||||
fake_info = types.SimpleNamespace(language="en", language_probability=0.99, duration=1.0)
|
||||
captured = {}
|
||||
|
||||
class FakeWhisperModel:
|
||||
def __init__(self, model_size, device, compute_type, local_files_only=False):
|
||||
captured["init"] = (model_size, device, compute_type, local_files_only)
|
||||
|
||||
def transcribe(self, path, language, task, beam_size, vad_filter):
|
||||
return [FakeSegment()], fake_info
|
||||
|
||||
fake_backend = build_stub_module("faster_whisper", WhisperModel=FakeWhisperModel)
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
source = Path(temp_dir) / "input.wav"
|
||||
with wave.open(str(source), "wb") as wav:
|
||||
wav.setnchannels(1)
|
||||
wav.setsampwidth(2)
|
||||
wav.setframerate(16000)
|
||||
wav.writeframes(b"\0\0" * 1600)
|
||||
|
||||
with mock.patch.dict(sys.modules, {"faster_whisper": fake_backend}, clear=False):
|
||||
transcript, _, raw_trace = module.ThinkingLLM_Whisper_ASR().process(
|
||||
model_size="small",
|
||||
language="auto",
|
||||
task="transcribe",
|
||||
device="cpu",
|
||||
compute_type="int8",
|
||||
beam_size=5,
|
||||
vad_filter=True,
|
||||
audio_file_path=str(source),
|
||||
)
|
||||
|
||||
self.assertEqual(transcript, "cached transcript")
|
||||
self.assertEqual(captured["init"], ("small", "cpu", "int8", True))
|
||||
self.assertIn("local_files_only=True", raw_trace)
|
||||
|
||||
def test_whisper_asr_downloads_once_when_cache_is_missing(self):
|
||||
module = load_module_from_file(
|
||||
"AILab_WhisperASR.py",
|
||||
"thinkingllm_whisper_cache_miss_test",
|
||||
)
|
||||
|
||||
class FakeSegment:
|
||||
start = 0.0
|
||||
end = 1.0
|
||||
text = " downloaded transcript "
|
||||
|
||||
fake_info = types.SimpleNamespace(language="en", language_probability=0.99, duration=1.0)
|
||||
attempts = []
|
||||
|
||||
class FakeWhisperModel:
|
||||
def __init__(self, model_size, device, compute_type, local_files_only=False):
|
||||
attempts.append(local_files_only)
|
||||
if local_files_only:
|
||||
raise RuntimeError("local_files_only=True and no cached snapshot was found")
|
||||
|
||||
def transcribe(self, path, language, task, beam_size, vad_filter):
|
||||
return [FakeSegment()], fake_info
|
||||
|
||||
fake_backend = build_stub_module("faster_whisper", WhisperModel=FakeWhisperModel)
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
source = Path(temp_dir) / "input.wav"
|
||||
with wave.open(str(source), "wb") as wav:
|
||||
wav.setnchannels(1)
|
||||
wav.setsampwidth(2)
|
||||
wav.setframerate(16000)
|
||||
wav.writeframes(b"\0\0" * 1600)
|
||||
|
||||
with mock.patch.dict(sys.modules, {"faster_whisper": fake_backend}, clear=False):
|
||||
transcript, _, raw_trace = module.ThinkingLLM_Whisper_ASR().process(
|
||||
model_size="small",
|
||||
language="auto",
|
||||
task="transcribe",
|
||||
device="cpu",
|
||||
compute_type="int8",
|
||||
beam_size=5,
|
||||
vad_filter=True,
|
||||
audio_file_path=str(source),
|
||||
)
|
||||
|
||||
self.assertEqual(transcript, "downloaded transcript")
|
||||
self.assertEqual(attempts, [True, False])
|
||||
self.assertIn("local_files_only=False", raw_trace)
|
||||
self.assertIn("Local Whisper cache miss", raw_trace)
|
||||
|
||||
def test_audio_to_wav_base64_resamples_to_16khz_mono(self):
|
||||
import numpy as real_numpy
|
||||
|
||||
|
||||
Reference in New Issue
Block a user