diff --git a/libraries/microsoft-agents-a365-runtime/microsoft_agents_a365/runtime/utility.py b/libraries/microsoft-agents-a365-runtime/microsoft_agents_a365/runtime/utility.py index 3e93f631..bd5341b9 100644 --- a/libraries/microsoft-agents-a365-runtime/microsoft_agents_a365/runtime/utility.py +++ b/libraries/microsoft-agents-a365-runtime/microsoft_agents_a365/runtime/utility.py @@ -9,7 +9,9 @@ from __future__ import annotations +import platform import uuid +from importlib.metadata import PackageNotFoundError, version from typing import Any, Optional import jwt @@ -23,6 +25,8 @@ class Utility: and other utility functions used across the Agent 365 runtime. """ + _cached_version = None + @staticmethod def get_app_id_from_token(token: Optional[str]) -> str: """ @@ -80,3 +84,27 @@ def resolve_agent_identity(context: Any, auth_token: Optional[str]) -> str: # Fallback to extracting App ID from the auth token return Utility.get_app_id_from_token(auth_token) + + @staticmethod + def get_user_agent_header(orchestrator: str = "") -> str: + """ + Generates a User-Agent header string for SDK requests. + + Args: + orchestrator: Optional orchestrator name to include in the User-Agent header. + Defaults to empty string if not provided. + + Returns: + str: A formatted User-Agent header string containing SDK version, OS type, + Python version, and optional orchestrator information. + """ + if Utility._cached_version is None: + try: + Utility._cached_version = version("microsoft-agents-a365-runtime") + except PackageNotFoundError: + Utility._cached_version = "unknown" + + orchestrator_part = f"; {orchestrator}" if orchestrator else "" + os_type = platform.system() + python_version = platform.python_version() + return f"Agent365SDK/{Utility._cached_version} ({os_type}; Python {python_version}{orchestrator_part})" diff --git a/tests/runtime/test_utility.py b/tests/runtime/test_utility.py index 2ce17e2e..54ad232d 100644 --- a/tests/runtime/test_utility.py +++ b/tests/runtime/test_utility.py @@ -3,6 +3,8 @@ """Unit tests for Utility class.""" +import platform +import re import uuid from unittest.mock import Mock @@ -124,3 +126,28 @@ def test_resolve_agent_identity_exception_handling(create_test_jwt, mock_context result = Utility.resolve_agent_identity(context, token) assert result == "token-app-id" + + +def test_get_user_agent_header_default(): + """Test get_user_agent_header returns expected format with default orchestrator.""" + os_type = platform.system() + py_version = platform.python_version() + + result = Utility.get_user_agent_header() + + # Regex for Agent365SDK/version (OS; Python version) + pattern = rf"^Agent365SDK/.+ \({os_type}; Python {py_version}\)$" + assert re.match(pattern, result) + + +def test_get_user_agent_header_with_orchestrator(): + """Test get_user_agent_header includes orchestrator when provided.""" + orchestrator = "TestOrchestrator" + os_type = platform.system() + py_version = platform.python_version() + + result = Utility.get_user_agent_header(orchestrator) + + # Regex for Agent365SDK/version (OS; Python version; TestOrchestrator) + pattern = rf"^Agent365SDK/.+ \({os_type}; Python {py_version}; {orchestrator}\)$" + assert re.match(pattern, result)