Skip to content

Commit 82cf06e

Browse files
committed
fix: prevent memory leak by checking existing hooks
Avoid duplicate hook registration by checking if a ContextVar with the same name is already registered in _configure_hooks. This prevents the memory leak while maintaining the original API and import patterns. Changes: - Check _configure_hooks before registering new hooks - Maintain original function signature and behavior - Use proper token-based context variable management - Keep imports inside function to satisfy linting
1 parent 3a8f7c1 commit 82cf06e

File tree

1 file changed

+19
-19
lines changed
  • libs/core/langchain_core/callbacks

1 file changed

+19
-19
lines changed

libs/core/langchain_core/callbacks/usage.py

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -88,16 +88,9 @@ def on_llm_end(self, response: LLMResult, **kwargs: Any) -> None:
8888
)
8989

9090

91-
# Module-level ContextVar that gets registered only once when the module is loaded
92-
_usage_metadata_callback_var: ContextVar[Optional[UsageMetadataCallbackHandler]] = (
93-
ContextVar("usage_metadata_callback", default=None)
94-
)
95-
_hook_registered = False
96-
97-
9891
@contextmanager
9992
def get_usage_metadata_callback(
100-
name: str = "usage_metadata_callback", # Parameter kept for backward compatibility but not used
93+
name: str = "usage_metadata_callback",
10194
) -> Generator[UsageMetadataCallbackHandler, None, None]:
10295
"""Get usage metadata callback.
10396
@@ -106,9 +99,7 @@ def get_usage_metadata_callback(
10699
107100
Args:
108101
name (str): The name of the context variable. Defaults to
109-
``'usage_metadata_callback'``. Note: This parameter is kept for
110-
backward compatibility but is no longer used, as the context variable
111-
is now defined at module level to prevent memory leaks.
102+
``'usage_metadata_callback'``.
112103
113104
Example:
114105
.. code-block:: python
@@ -139,16 +130,25 @@ def get_usage_metadata_callback(
139130
.. versionadded:: 0.3.49
140131
141132
"""
142-
# Register hook only once to prevent memory leak
143-
global _hook_registered
144-
if not _hook_registered:
145-
from langchain_core.tracers.context import register_configure_hook
146-
register_configure_hook(_usage_metadata_callback_var, inheritable=True)
147-
_hook_registered = True
133+
from langchain_core.tracers.context import register_configure_hook, _configure_hooks
134+
135+
# Check if this ContextVar is already registered to prevent memory leak
136+
usage_metadata_callback_var: ContextVar[Optional[UsageMetadataCallbackHandler]] = (
137+
ContextVar(name, default=None)
138+
)
139+
140+
# Only register if not already in the hooks list
141+
already_registered = any(
142+
hook[0].name == name for hook in _configure_hooks
143+
if hasattr(hook[0], 'name')
144+
)
145+
146+
if not already_registered:
147+
register_configure_hook(usage_metadata_callback_var, inheritable=True)
148148

149149
cb = UsageMetadataCallbackHandler()
150-
token = _usage_metadata_callback_var.set(cb)
150+
token = usage_metadata_callback_var.set(cb)
151151
try:
152152
yield cb
153153
finally:
154-
_usage_metadata_callback_var.reset(token)
154+
usage_metadata_callback_var.reset(token)

0 commit comments

Comments
 (0)