From 9425ade13c62808701729bfbbe8c03cc0dfe48ad Mon Sep 17 00:00:00 2001 From: Tom Yu Date: Thu, 8 Jan 2026 01:20:50 +0800 Subject: [PATCH 1/4] fix(genai): compatibility with Python 3.8 hashlib usage Added compatible_hashlib module to fix Python 3.8 hashlib function parameter compatibility issues Replaced hashlib imports in fs_uploader and completion_hook with compatible versions Used typing_extensions TypeAlias in completion_hook to enhance type compatibility Optimized Token Histogram attribute dictionary merging in metrics to ensure compatibility and semantic correctness --- .../genai/_multimodal_upload/fs_uploader.py | 2 +- .../util/genai/_upload/completion_hook.py | 11 ++-- .../util/genai/compatible_hashlib.py | 51 +++++++++++++++++++ .../src/opentelemetry/util/genai/metrics.py | 2 +- 4 files changed, 61 insertions(+), 5 deletions(-) create mode 100644 util/opentelemetry-util-genai/src/opentelemetry/util/genai/compatible_hashlib.py diff --git a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_multimodal_upload/fs_uploader.py b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_multimodal_upload/fs_uploader.py index 1b22ab043..b7f468dad 100644 --- a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_multimodal_upload/fs_uploader.py +++ b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_multimodal_upload/fs_uploader.py @@ -19,7 +19,6 @@ from __future__ import annotations -import hashlib import io import json import logging @@ -36,6 +35,7 @@ import httpx from opentelemetry.instrumentation.utils import suppress_http_instrumentation +from opentelemetry.util.genai import compatible_hashlib as hashlib from opentelemetry.util.genai._multimodal_upload._base import ( Uploader, UploadItem, diff --git a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_upload/completion_hook.py b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_upload/completion_hook.py index 4f0b98e6f..adb4dd0cf 100644 --- a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_upload/completion_hook.py +++ b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_upload/completion_hook.py @@ -15,7 +15,6 @@ from __future__ import annotations -import hashlib import logging import posixpath import threading @@ -30,9 +29,13 @@ import fsspec +# Aliyun Python Agent Extension: Add support for Python 3.8 +from typing_extensions import TypeAlias + from opentelemetry._logs import LogRecord from opentelemetry.semconv._incubating.attributes import gen_ai_attributes from opentelemetry.trace import Span +from opentelemetry.util.genai import compatible_hashlib as hashlib from opentelemetry.util.genai import types from opentelemetry.util.genai.completion_hook import CompletionHook from opentelemetry.util.genai.utils import gen_ai_json_dump @@ -72,10 +75,12 @@ class CompletionRefs: system_instruction_ref: str -JsonEncodeable = list[dict[str, Any]] +# Aliyun Python Agent Extension: Use TypeAlias with string annotation for Python 3.8 compatibility +JsonEncodeable: TypeAlias = "list[dict[str, Any]]" # mapping of upload path and whether the contents were hashed to the filename to function computing upload data dict -UploadData = dict[tuple[str, bool], Callable[[], JsonEncodeable]] +# Aliyun Python Agent Extension: Use TypeAlias with string annotation for Python 3.8 compatibility +UploadData: TypeAlias = "dict[tuple[str, bool], Callable[[], JsonEncodeable]]" def is_system_instructions_hashable( diff --git a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/compatible_hashlib.py b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/compatible_hashlib.py new file mode 100644 index 000000000..9feb9eb5d --- /dev/null +++ b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/compatible_hashlib.py @@ -0,0 +1,51 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import hashlib as _hashlib +import sys +from hashlib import * # noqa: F403 # pylint: disable=wildcard-import,unused-wildcard-import # pyright: ignore[reportWildcardImportFromLibrary] + + +def _make_compatible(orig_func): # type: ignore + def compatible_func(content: bytes = b"", *, usedforsecurity: bool = True): # type: ignore + return orig_func(content) # type: ignore + + return compatible_func + + +def _setup_hashlib_compatibility(): + """Setup hashlib compatibility to support Python 3.8""" + if sys.version_info < (3, 9): + # List of hash functions that need usedforsecurity parameter support + functions_to_patch = [ + "md5", + "sha1", + "sha224", + "sha256", + "sha384", + "sha512", + ] + + for func_name in functions_to_patch: + if hasattr(_hashlib, func_name): + original_func = getattr(_hashlib, func_name) + # Apply patch + compatible_func = _make_compatible(original_func) + setattr(_hashlib, func_name, compatible_func) + + # Also update the reference in the current module + globals()[func_name] = compatible_func + + +_setup_hashlib_compatibility() diff --git a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/metrics.py b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/metrics.py index 68f8d08d3..e50244217 100644 --- a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/metrics.py +++ b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/metrics.py @@ -90,7 +90,7 @@ def record( for token_count, token_type in token_counts: self._token_histogram.record( token_count, - attributes=attributes | {GenAI.GEN_AI_TOKEN_TYPE: token_type}, + attributes={**attributes, GenAI.GEN_AI_TOKEN_TYPE: token_type}, context=span_context, ) From a95ae749ac8acfa5cd5eb83e67356a4a896d69ac Mon Sep 17 00:00:00 2001 From: Tom Yu Date: Thu, 8 Jan 2026 01:24:35 +0800 Subject: [PATCH 2/4] add changelog --- util/opentelemetry-util-genai/CHANGELOG-loongsuite.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/util/opentelemetry-util-genai/CHANGELOG-loongsuite.md b/util/opentelemetry-util-genai/CHANGELOG-loongsuite.md index b845f9911..7b78fa67f 100644 --- a/util/opentelemetry-util-genai/CHANGELOG-loongsuite.md +++ b/util/opentelemetry-util-genai/CHANGELOG-loongsuite.md @@ -7,5 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +- Add support for memory operations. ([#83](https://github.com/alibaba/loongsuite-python-agent/pull/83)) + - Add multimodal separation and upload support for GenAI utils. ([#94](https://github.com/alibaba/loongsuite-python-agent/pull/94)) -- Add support for memory operations. ([#83](https://github.com/alibaba/loongsuite-python-agent/pull/83)) \ No newline at end of file + +- Fix compatibility with Python 3.8 hashlib usage. ([#102](https://github.com/alibaba/loongsuite-python-agent/pull/102)) From fdf72b8705c5e9e1f26a81097b702dd717e23e5e Mon Sep 17 00:00:00 2001 From: Tom Yu Date: Thu, 8 Jan 2026 09:45:24 +0800 Subject: [PATCH 3/4] update comment --- .../util/genai/_multimodal_upload/fs_uploader.py | 2 ++ .../opentelemetry/util/genai/_upload/completion_hook.py | 8 +++++--- .../src/opentelemetry/util/genai/metrics.py | 5 ++++- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_multimodal_upload/fs_uploader.py b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_multimodal_upload/fs_uploader.py index b7f468dad..bcba19582 100644 --- a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_multimodal_upload/fs_uploader.py +++ b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_multimodal_upload/fs_uploader.py @@ -35,6 +35,8 @@ import httpx from opentelemetry.instrumentation.utils import suppress_http_instrumentation + +# LoongSuite Extension: For Python 3.8 Compatibility from opentelemetry.util.genai import compatible_hashlib as hashlib from opentelemetry.util.genai._multimodal_upload._base import ( Uploader, diff --git a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_upload/completion_hook.py b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_upload/completion_hook.py index adb4dd0cf..a15171b9d 100644 --- a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_upload/completion_hook.py +++ b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/_upload/completion_hook.py @@ -29,12 +29,14 @@ import fsspec -# Aliyun Python Agent Extension: Add support for Python 3.8 +# LoongSuite Extension: For Python 3.8 Compatibility from typing_extensions import TypeAlias from opentelemetry._logs import LogRecord from opentelemetry.semconv._incubating.attributes import gen_ai_attributes from opentelemetry.trace import Span + +# LoongSuite Extension: For Python 3.8 Compatibility from opentelemetry.util.genai import compatible_hashlib as hashlib from opentelemetry.util.genai import types from opentelemetry.util.genai.completion_hook import CompletionHook @@ -75,11 +77,11 @@ class CompletionRefs: system_instruction_ref: str -# Aliyun Python Agent Extension: Use TypeAlias with string annotation for Python 3.8 compatibility +# LoongSuite Extension: Use TypeAlias with string annotation for Python 3.8 compatibility JsonEncodeable: TypeAlias = "list[dict[str, Any]]" # mapping of upload path and whether the contents were hashed to the filename to function computing upload data dict -# Aliyun Python Agent Extension: Use TypeAlias with string annotation for Python 3.8 compatibility +# LoongSuite Extension: Use TypeAlias with string annotation for Python 3.8 compatibility UploadData: TypeAlias = "dict[tuple[str, bool], Callable[[], JsonEncodeable]]" diff --git a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/metrics.py b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/metrics.py index e50244217..0e174a9eb 100644 --- a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/metrics.py +++ b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/metrics.py @@ -90,7 +90,10 @@ def record( for token_count, token_type in token_counts: self._token_histogram.record( token_count, - attributes={**attributes, GenAI.GEN_AI_TOKEN_TYPE: token_type}, + attributes={ + **attributes, + GenAI.GEN_AI_TOKEN_TYPE: token_type, + }, # LoongSuite Extension: For Python 3.8 Compatibility context=span_context, ) From 0d5cd81de3fb016e77b5ac34b372a5279235aae0 Mon Sep 17 00:00:00 2001 From: Tom Yu Date: Thu, 8 Jan 2026 10:24:45 +0800 Subject: [PATCH 4/4] fix compatibility --- .../src/opentelemetry/util/genai/types.py | 6 ++-- .../tests/test_extended_handler.py | 36 ++++++++----------- 2 files changed, 17 insertions(+), 25 deletions(-) diff --git a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/types.py b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/types.py index b8fca6ca2..1c100da35 100644 --- a/util/opentelemetry-util-genai/src/opentelemetry/util/genai/types.py +++ b/util/opentelemetry-util-genai/src/opentelemetry/util/genai/types.py @@ -14,21 +14,21 @@ from __future__ import annotations -from contextvars import Token +from contextvars import Token # pylint: disable=W0611 from dataclasses import dataclass, field from enum import Enum from typing import Any, Literal, Type, Union from typing_extensions import TypeAlias -from opentelemetry.context import Context +from opentelemetry.context import Context # pylint: disable=W0611 from opentelemetry.semconv._incubating.attributes import ( gen_ai_attributes as GenAI, ) from opentelemetry.trace import Span # LoongSuite Extension: Add type alias for ContextToken to avoid failure in python 3.8 -ContextToken: TypeAlias = Token[Context] +ContextToken: TypeAlias = "Token[Context]" class ContentCapturingMode(Enum): diff --git a/util/opentelemetry-util-genai/tests/test_extended_handler.py b/util/opentelemetry-util-genai/tests/test_extended_handler.py index 0ba431c3d..0f046b379 100644 --- a/util/opentelemetry-util-genai/tests/test_extended_handler.py +++ b/util/opentelemetry-util-genai/tests/test_extended_handler.py @@ -1388,17 +1388,13 @@ def test_fallback_methods_apply_attributes(self): inv.span = mock_span error = Error(message="err", type=ValueError) - with ( - patch( - "opentelemetry.util.genai._multimodal_processing._apply_llm_finish_attributes" - ) as m1, - patch( - "opentelemetry.util.genai._multimodal_processing._apply_error_attributes" - ) as m2, - patch( - "opentelemetry.util.genai._multimodal_processing._maybe_emit_llm_event" - ), - ): + with patch( + "opentelemetry.util.genai._multimodal_processing._apply_llm_finish_attributes" + ) as m1, patch( + "opentelemetry.util.genai._multimodal_processing._apply_error_attributes" + ) as m2, patch( + "opentelemetry.util.genai._multimodal_processing._maybe_emit_llm_event" + ): # fmt: skip handler._fallback_end_span(inv) m1.assert_called_with(mock_span, inv) mock_span.end.assert_called_once() @@ -1428,17 +1424,13 @@ def test_async_stop_and_fail_llm_process_correctly(self): ) ] - with ( - patch( - "opentelemetry.util.genai._multimodal_processing._apply_llm_finish_attributes" - ) as m1, - patch( - "opentelemetry.util.genai._multimodal_processing._apply_error_attributes" - ) as m2, - patch( - "opentelemetry.util.genai._multimodal_processing._maybe_emit_llm_event" - ), - ): + with patch( + "opentelemetry.util.genai._multimodal_processing._apply_llm_finish_attributes" + ) as m1, patch( + "opentelemetry.util.genai._multimodal_processing._apply_error_attributes" + ) as m2, patch( + "opentelemetry.util.genai._multimodal_processing._maybe_emit_llm_event" + ): # fmt: skip handler._async_stop_llm( _MultimodalAsyncTask( invocation=inv, method="stop", handler=handler