fix(core): clear usage metadata callback on exceptions in context manager (#39616)

Co-authored-by: Solaris-star <820622658@qq.com>
This commit is contained in:
ccurmeandSolaris-star authored and GitHub committed 2026-08-12 13:32:51 -04:00
1 parent 2d47a5f398
commit 0a86934ab5
2 files changed
+21 -3

No files matched your search

+8 -3
View File
@@ -114,6 +114,11 @@ def get_usage_metadata_callback(
)
register_configure_hook(usage_metadata_callback_var, inheritable=True)
cb = UsageMetadataCallbackHandler()
usage_metadata_callback_var.set(cb)
yield cb
usage_metadata_callback_var.set(None)
token = usage_metadata_callback_var.set(cb)
try:
yield cb
finally:
# Always clear the context var, including when the with-block raises.
# Without finally, post-block model calls keep accumulating into cb
# (see #38989).
usage_metadata_callback_var.reset(token)
@@ -1,3 +1,4 @@
import contextlib
from typing import Any
from langchain_core.callbacks import (
@@ -120,3 +121,15 @@ async def test_usage_callback_async() -> None:
callback = UsageMetadataCallbackHandler()
_ = await llm.abatch(["Message 1", "Message 2"], config={"callbacks": [callback]})
assert callback.usage_metadata == {"test_model": total_1_2}
def test_usage_callback_clears_on_exception() -> None:
"""Callback must stop tracking after with-block exits via exception (#38989)."""
llm = FakeChatModelWithResponseMetadata(messages=iter(messages), model_name="fake")
with contextlib.suppress(RuntimeError), get_usage_metadata_callback() as cb:
_ = llm.invoke("in block")
raise RuntimeError
# Calls after the block must not accumulate into the previous callback.
_ = llm.invoke("outside block")
assert cb.usage_metadata == {"fake": usage1}