Skip to content

Commit 5456d4e

Browse files
committed
fix: resolve VRAM leak in multimodal models by explicitly closing mtmd context
- Remove the `ExitStack` closure in `Llava15ChatHandler` to break circular references preventing garbage collection of the vision context. - Implement explicit `close()` and `__del__()` methods in the chat handler to safely free `mtmd_ctx`. - Integrate `chat_handler.close()` into the main `Llama.close()` lifecycle and nullify related attributes for immediate memory reclamation.
1 parent 17e274c commit 5456d4e

2 files changed

Lines changed: 27 additions & 15 deletions

File tree

llama_cpp/llama.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -634,8 +634,12 @@ def close(self) -> None:
634634
self._candidates.close()
635635
self._candidates = None
636636

637+
if hasattr(self, "chat_handler") and hasattr(self.chat_handler, "close"):
638+
self.chat_handler.close()
639+
637640
self.model_params =None
638641
self.context_params = None
642+
self.chat_handler = None
639643
self.input_ids = None
640644
self.scores = None
641645
self.tokenizer_ = None

llama_cpp/llama_chat_format.py

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2850,24 +2850,24 @@ def _init_mtmd_context(self, llama_model: llama.Llama):
28502850
self._mtmd_cpp.mtmd_helper_log_set(llama_log_callback, ctypes.c_void_p(0))
28512851

28522852
# Get default parameters
2853-
mctx_params = self._mtmd_cpp.mtmd_context_params_default()
2854-
mctx_params.use_gpu = self.use_gpu
2855-
mctx_params.print_timings = self.verbose
2856-
mctx_params.n_threads = llama_model.n_threads
2857-
mctx_params.flash_attn_type = self._mtmd_cpp.clip_flash_attn_type.CLIP_FLASH_ATTN_TYPE_AUTO
2858-
mctx_params.warmup = True
2853+
self.mctx_params = self._mtmd_cpp.mtmd_context_params_default()
2854+
self.mctx_params.use_gpu = self.use_gpu
2855+
self.mctx_params.print_timings = self.verbose
2856+
self.mctx_params.n_threads = llama_model.n_threads
2857+
self.mctx_params.flash_attn_type = self._mtmd_cpp.clip_flash_attn_type.CLIP_FLASH_ATTN_TYPE_AUTO
2858+
self.mctx_params.warmup = True
28592859
if self.image_min_tokens > 0:
2860-
mctx_params.image_min_tokens = self.image_min_tokens
2860+
self.mctx_params.image_min_tokens = self.image_min_tokens
28612861
if self.image_max_tokens > 0:
2862-
mctx_params.image_max_tokens = self.image_max_tokens
2862+
self.mctx_params.image_max_tokens = self.image_max_tokens
28632863
if (self.image_max_tokens < self.image_min_tokens) and self.image_max_tokens > 0:
28642864
raise ValueError(f"image_max_pixels {self.image_max_tokens} is less than image_min_pixels {self.image_min_tokens}")
28652865

28662866
# Initialize mtmd context
28672867
self.mtmd_ctx = self._mtmd_cpp.mtmd_init_from_file(
28682868
self.clip_model_path.encode(),
28692869
llama_model.model,
2870-
mctx_params
2870+
self.mctx_params
28712871
)
28722872

28732873
if self.mtmd_ctx is None:
@@ -2877,13 +2877,21 @@ def _init_mtmd_context(self, llama_model: llama.Llama):
28772877
if not self._mtmd_cpp.mtmd_support_vision(self.mtmd_ctx):
28782878
raise ValueError("Vision is not supported by this model")
28792879

2880-
def mtmd_free():
2881-
with suppress_stdout_stderr(disable=self.verbose):
2882-
if self.mtmd_ctx is not None:
2883-
self._mtmd_cpp.mtmd_free(self.mtmd_ctx)
2884-
self.mtmd_ctx = None
2880+
def close(self) -> None:
2881+
"""Explicitly free the mtmd context and vision model resources."""
2882+
if getattr(self, "mtmd_ctx", None) is not None:
2883+
try:
2884+
with suppress_stdout_stderr(disable=getattr(self, "verbose", True)):
2885+
self._mtmd_cpp.mtmd_free(self.mtmd_ctx)
2886+
except Exception:
2887+
pass
2888+
self.mtmd_ctx = None
2889+
self.mctx_params = None
2890+
2891+
self._exit_stack.close()
28852892

2886-
self._exit_stack.callback(mtmd_free)
2893+
def __del__(self) -> None:
2894+
self.close()
28872895

28882896
def load_image(self, image_url: str) -> bytes:
28892897
return self._load_image(image_url)

0 commit comments

Comments
 (0)