Release 2.3.7 Whisper offline cache fix

This commit is contained in:
Marcel Hofer
2026-06-14 19:36:38 +02:00
parent 028b2df006
commit 13737fae2d
5 changed files with 133 additions and 6 deletions
+34 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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"]
+96 -1
View File
@@ -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