From 9fe1960a7c107190e4ed075cb0d440dc77ee5c29 Mon Sep 17 00:00:00 2001 From: VascoSch92 Date: Wed, 27 May 2026 11:16:19 +0200 Subject: [PATCH] fix(sdk): snapshot LLM metrics via Metrics.get_snapshot() The per-response MetricsSnapshot was hand-built and passed the live accumulated_token_usage by reference, so it aliased mutable LLM state instead of capturing it. Call the canonical Metrics.get_snapshot() (which deep-copies accumulated_token_usage) directly in the two LLMResponse builders and drop the redundant _current_metrics_snapshot wrapper. --- openhands-sdk/openhands/sdk/llm/llm.py | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/openhands-sdk/openhands/sdk/llm/llm.py b/openhands-sdk/openhands/sdk/llm/llm.py index df857d49d1..cf2aeab361 100644 --- a/openhands-sdk/openhands/sdk/llm/llm.py +++ b/openhands-sdk/openhands/sdk/llm/llm.py @@ -105,7 +105,7 @@ ) from openhands.sdk.llm.utils.image_resize import maybe_resize_messages_for_provider from openhands.sdk.llm.utils.litellm_provider import infer_litellm_provider -from openhands.sdk.llm.utils.metrics import Metrics, MetricsSnapshot +from openhands.sdk.llm.utils.metrics import Metrics from openhands.sdk.llm.utils.model_features import get_features from openhands.sdk.llm.utils.retry_mixin import RetryMixin from openhands.sdk.llm.utils.telemetry import Telemetry @@ -757,15 +757,6 @@ async def _ahandle_error( # Shared helpers for completion / acompletion / responses / aresponses # ========================================================================= - def _current_metrics_snapshot(self) -> MetricsSnapshot: - """Snapshot current LLM metrics for an :class:`LLMResponse`.""" - return MetricsSnapshot( - model_name=self.metrics.model_name, - accumulated_cost=self.metrics.accumulated_cost, - max_budget_per_task=self.metrics.max_budget_per_task, - accumulated_token_usage=self.metrics.accumulated_token_usage, - ) - def _make_retry_decorator( self, ) -> Callable[[Callable[..., Any]], Callable[..., Any]]: @@ -785,7 +776,7 @@ def _build_completion_result(self, resp: ModelResponse) -> LLMResponse: message = Message.from_llm_chat_message(first_choice["message"]) return LLMResponse( message=message, - metrics=self._current_metrics_snapshot(), + metrics=self.metrics.get_snapshot(), raw_response=resp, ) @@ -795,7 +786,7 @@ def _build_responses_result(self, resp: ResponsesAPIResponse) -> LLMResponse: message = Message.from_llm_responses_output(output_seq) return LLMResponse( message=message, - metrics=self._current_metrics_snapshot(), + metrics=self.metrics.get_snapshot(), raw_response=resp, )