From 7c7c589ade55dfc7ad8312a09ff45d69d6c30fca Mon Sep 17 00:00:00 2001 From: apex-mochen <2756823972@qq.com> Date: Sun, 11 Oct 2026 10:24:10 +0800 Subject: [PATCH] fix(langfuse): read standard chat fields only for dict usage in responses logging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit test_log_langfuse_v2_malformed_responses_api_usage_still_logs expects a usage dict whose token counts fail validation to log as zero usage. The new `_usage_token_value` helper's dict branch fell back to decisions-style names (`input_tokens`/`output_tokens`), resurrecting `input_tokens: 16` from the raw dict `_chat_usage` passes through when transform raises ValueError. Keep the decisions fallback on the non-dict branch only (Pydantic decisions usage models), and read the standard chat field for dicts — matching the original `getattr(..., None) or 0` semantics. Also place `# cast-ok:` on the cast call's first line so the type-discipline gate counts it as used (previously folded onto the continuation line, tripping LIT006 + LIT013). Verified with scripts/check_type_discipline.py: no LIT006/LIT013 on the touched lines; remaining violations are main's pre-existing budgeted counts. Signed-off-by: apex-mochen <2756823972@qq.com> --- litellm/integrations/langfuse/langfuse.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/litellm/integrations/langfuse/langfuse.py b/litellm/integrations/langfuse/langfuse.py index 20e1a4182fd..19aa8a99f40 100644 --- a/litellm/integrations/langfuse/langfuse.py +++ b/litellm/integrations/langfuse/langfuse.py @@ -153,9 +153,13 @@ def _usage_token_value(usage_obj: object, standard_name: str, decision_name: str "_UsageObject", usage_obj ) ) - if (dict_value := usage_proto.get(standard_name)) is None and decision_name is not None: - decision_dict_value: Final = usage_proto.get(decision_name) - return decision_dict_value if isinstance(decision_dict_value, int) else 0 + # A raw dict here means _chat_usage could not validate it as a chat or + # responses usage (e.g. a malformed /v1/responses payload whose token + # counts fail validation) and passed it through unchanged. Read only the + # standard chat field so a failed validation logs as zero, matching + # getattr(..., None) or 0; do not fall back to decisions-style names, + # which would resurrect a value from a payload that failed validation. + dict_value: Final = usage_proto.get(standard_name) return dict_value if isinstance(dict_value, int) else 0 if (attr_value := getattr(usage_obj, standard_name, None)) is None and decision_name is not None: decision_attr_value: Final = getattr(usage_obj, decision_name, None)