microsoft-agents-a365-runtime 0.2.1.dev2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,19 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+
4
+ from .environment_utils import get_observability_authentication_scope
5
+ from .operation_error import OperationError
6
+ from .operation_result import OperationResult
7
+ from .power_platform_api_discovery import ClusterCategory, PowerPlatformApiDiscovery
8
+ from .utility import Utility
9
+
10
+ __all__ = [
11
+ "get_observability_authentication_scope",
12
+ "PowerPlatformApiDiscovery",
13
+ "ClusterCategory",
14
+ "Utility",
15
+ "OperationError",
16
+ "OperationResult",
17
+ ]
18
+
19
+ __path__ = __import__("pkgutil").extend_path(__path__, __name__)
@@ -0,0 +1,61 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+
4
+ """
5
+ Utility logic for environment-related operations.
6
+ """
7
+
8
+ import os
9
+
10
+ # Authentication scopes for different environments
11
+ PROD_OBSERVABILITY_SCOPE = "https://api.powerplatform.com/.default"
12
+
13
+ # Cluster categories for different environments
14
+ PROD_OBSERVABILITY_CLUSTER_CATEGORY = "prod"
15
+
16
+ # Default environment names
17
+ PRODUCTION_ENVIRONMENT_NAME = "production"
18
+ DEVELOPMENT_ENVIRONMENT_NAME = "Development"
19
+
20
+
21
+ def get_observability_authentication_scope() -> list[str]:
22
+ """
23
+ Returns the scope for authenticating to the observability service based on the current environment.
24
+
25
+ The scope can be overridden via the A365_OBSERVABILITY_SCOPE_OVERRIDE environment variable
26
+ to enable testing against pre-production environments.
27
+
28
+ Returns:
29
+ list[str]: The authentication scope for the current environment.
30
+ """
31
+ override_scope = os.getenv("A365_OBSERVABILITY_SCOPE_OVERRIDE", "").strip()
32
+ return [override_scope] if override_scope else [PROD_OBSERVABILITY_SCOPE]
33
+
34
+
35
+ def is_development_environment() -> bool:
36
+ """
37
+ Returns True if the current environment is a development environment.
38
+
39
+ Returns:
40
+ bool: True if the current environment is development, False otherwise.
41
+ """
42
+ environment = _get_current_environment()
43
+ return environment.lower() == DEVELOPMENT_ENVIRONMENT_NAME.lower()
44
+
45
+
46
+ def _get_current_environment() -> str:
47
+ """
48
+ Gets the current environment name.
49
+
50
+ Returns:
51
+ str: The current environment name.
52
+ """
53
+ # Check environment variables in order of precedence
54
+
55
+ # Check Python-specific environment variables
56
+ environment = os.getenv("PYTHON_ENVIRONMENT")
57
+ if environment:
58
+ return environment
59
+
60
+ # Default to Production
61
+ return PRODUCTION_ENVIRONMENT_NAME
@@ -0,0 +1,58 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+
4
+ """
5
+ Encapsulates an error from an operation.
6
+ """
7
+
8
+
9
+ class OperationError:
10
+ """
11
+ Represents an error that occurred during an operation.
12
+
13
+ This class wraps an exception and provides a consistent interface for
14
+ accessing error information.
15
+ """
16
+
17
+ def __init__(self, exception: Exception):
18
+ """
19
+ Initialize a new instance of the OperationError class.
20
+
21
+ Args:
22
+ exception: The exception associated with the error.
23
+
24
+ Raises:
25
+ ValueError: If exception is None.
26
+ """
27
+ if exception is None:
28
+ raise ValueError("exception cannot be None")
29
+ self._exception = exception
30
+
31
+ @property
32
+ def exception(self) -> Exception:
33
+ """
34
+ Get the exception associated with the error.
35
+
36
+ Returns:
37
+ Exception: The exception associated with the error.
38
+ """
39
+ return self._exception
40
+
41
+ @property
42
+ def message(self) -> str:
43
+ """
44
+ Get the message associated with the error.
45
+
46
+ Returns:
47
+ str: The error message from the exception.
48
+ """
49
+ return str(self._exception)
50
+
51
+ def __str__(self) -> str:
52
+ """
53
+ Return a string representation of the error.
54
+
55
+ Returns:
56
+ str: A string representation of the error.
57
+ """
58
+ return str(self._exception)
@@ -0,0 +1,98 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+
4
+ """
5
+ Represents the result of an operation.
6
+ """
7
+
8
+ from typing import List, Optional
9
+
10
+ from .operation_error import OperationError
11
+
12
+
13
+ class OperationResult:
14
+ """
15
+ Represents the result of an operation.
16
+
17
+ This class encapsulates the success or failure state of an operation along
18
+ with any associated errors.
19
+ """
20
+
21
+ _success_instance: Optional["OperationResult"] = None
22
+
23
+ def __init__(self, succeeded: bool, errors: Optional[List[OperationError]] = None):
24
+ """
25
+ Initialize a new instance of the OperationResult class.
26
+
27
+ Args:
28
+ succeeded: Flag indicating whether the operation succeeded.
29
+ errors: Optional list of errors that occurred during the operation.
30
+ """
31
+ self._succeeded = succeeded
32
+ self._errors = errors if errors is not None else []
33
+
34
+ @property
35
+ def succeeded(self) -> bool:
36
+ """
37
+ Get a flag indicating whether the operation succeeded.
38
+
39
+ Returns:
40
+ bool: True if the operation succeeded, otherwise False.
41
+ """
42
+ return self._succeeded
43
+
44
+ @property
45
+ def errors(self) -> List[OperationError]:
46
+ """
47
+ Get the list of errors that occurred during the operation.
48
+
49
+ Note:
50
+ This property returns a defensive copy of the internal error list
51
+ to prevent external modifications, which is especially important for
52
+ protecting the singleton instance returned by success().
53
+
54
+ Returns:
55
+ List[OperationError]: A copy of the list of operation errors.
56
+ """
57
+ return list(self._errors)
58
+
59
+ @staticmethod
60
+ def success() -> "OperationResult":
61
+ """
62
+ Return an OperationResult indicating a successful operation.
63
+
64
+ Returns:
65
+ OperationResult: An OperationResult indicating a successful operation.
66
+ """
67
+ return OperationResult._success_instance
68
+
69
+ @staticmethod
70
+ def failed(*errors: OperationError) -> "OperationResult":
71
+ """
72
+ Create an OperationResult indicating a failed operation.
73
+
74
+ Args:
75
+ *errors: Variable number of OperationError instances.
76
+
77
+ Returns:
78
+ OperationResult: An OperationResult indicating a failed operation.
79
+ """
80
+ error_list = list(errors) if errors else []
81
+ return OperationResult(succeeded=False, errors=error_list)
82
+
83
+ def __str__(self) -> str:
84
+ """
85
+ Convert the value of the current OperationResult object to its string representation.
86
+
87
+ Returns:
88
+ str: A string representation of the current OperationResult object.
89
+ """
90
+ if self._succeeded:
91
+ return "Succeeded"
92
+ else:
93
+ error_messages = ", ".join(str(error.message) for error in self._errors)
94
+ return f"Failed: {error_messages}" if error_messages else "Failed"
95
+
96
+
97
+ # Module-level eager initialization (thread-safe by Python's import lock)
98
+ OperationResult._success_instance = OperationResult(succeeded=True)
@@ -0,0 +1,90 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+
4
+ import re
5
+ from typing import Literal
6
+
7
+ ClusterCategory = Literal[
8
+ "local",
9
+ "dev",
10
+ "test",
11
+ "preprod",
12
+ "firstrelease",
13
+ "prod",
14
+ "gov",
15
+ "high",
16
+ "dod",
17
+ "mooncake",
18
+ "ex",
19
+ "rx",
20
+ ]
21
+
22
+
23
+ class PowerPlatformApiDiscovery:
24
+ """Discovery helper for Power Platform API endpoints."""
25
+
26
+ def __init__(self, cluster_category: ClusterCategory) -> None:
27
+ self.cluster_category = cluster_category
28
+
29
+ def get_token_audience(self) -> str:
30
+ return f"https://{self._get_environment_api_host_name_suffix()}"
31
+
32
+ def get_token_endpoint_host(self) -> str:
33
+ return self._get_environment_api_host_name_suffix()
34
+
35
+ def get_tenant_endpoint(self, tenant_id: str) -> str:
36
+ return self._generate_power_platform_api_domain(tenant_id)
37
+
38
+ def get_tenant_island_cluster_endpoint(self, tenant_id: str) -> str:
39
+ return self._generate_power_platform_api_domain(tenant_id, "il-")
40
+
41
+ def _generate_power_platform_api_domain(
42
+ self, host_name_identifier: str, host_name_prefix: str = ""
43
+ ) -> str:
44
+ # Validate allowed characters: alphanumeric and dash
45
+ if not re.match(r"^[a-zA-Z0-9-]+$", host_name_identifier):
46
+ raise ValueError(
47
+ f"Cannot generate Power Platform API endpoint because the tenant identifier contains invalid host name characters, only alphanumeric and dash characters are expected: {host_name_identifier}"
48
+ )
49
+
50
+ host_name_infix = "tenant"
51
+ hex_name_suffix_length = self._get_hex_api_suffix_length()
52
+ hex_name = host_name_identifier.lower().replace("-", "")
53
+
54
+ if hex_name_suffix_length >= len(hex_name):
55
+ raise ValueError(
56
+ f"Cannot generate Power Platform API endpoint because the normalized tenant identifier must be at least {hex_name_suffix_length + 1} "
57
+ f"characters in length: {hex_name}"
58
+ )
59
+
60
+ hex_name_suffix = hex_name[-hex_name_suffix_length:]
61
+ hex_name_prefix = hex_name[: len(hex_name) - hex_name_suffix_length]
62
+ host_name_suffix = self._get_environment_api_host_name_suffix()
63
+
64
+ return f"{host_name_prefix}{hex_name_prefix}.{hex_name_suffix}.{host_name_infix}.{host_name_suffix}"
65
+
66
+ def _get_hex_api_suffix_length(self) -> int:
67
+ if self.cluster_category in ("firstrelease", "prod"):
68
+ return 2
69
+ return 1
70
+
71
+ def _get_environment_api_host_name_suffix(self) -> str:
72
+ cluster_to_suffix = {
73
+ "local": "api.powerplatform.localhost",
74
+ "dev": "api.powerplatform.com", # defaulting to prod
75
+ "test": "api.powerplatform.com", # defaulting to prod
76
+ "preprod": "api.powerplatform.com", # defaulting to prod
77
+ "firstrelease": "api.powerplatform.com",
78
+ "prod": "api.powerplatform.com",
79
+ "gov": "api.gov.powerplatform.microsoft.us",
80
+ "high": "api.high.powerplatform.microsoft.us",
81
+ "dod": "api.appsplatform.us",
82
+ "mooncake": "api.powerplatform.partner.microsoftonline.cn",
83
+ "ex": "api.powerplatform.eaglex.ic.gov",
84
+ "rx": "api.powerplatform.microsoft.scloud",
85
+ }
86
+ cc = self.cluster_category
87
+ try:
88
+ return cluster_to_suffix[cc]
89
+ except KeyError as exc:
90
+ raise ValueError(f"Invalid ClusterCategory value: {self.cluster_category}") from exc
@@ -0,0 +1,111 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+
4
+ """
5
+ Utility functions for Microsoft Agent 365 runtime operations.
6
+
7
+ This module provides utility functions for token handling, agent identity resolution,
8
+ and other common runtime operations.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import platform
14
+ import uuid
15
+ from importlib.metadata import PackageNotFoundError, version
16
+ from typing import Any, Optional
17
+
18
+ import jwt
19
+
20
+
21
+ class Utility:
22
+ """
23
+ Utility class providing common runtime operations for Agent 365.
24
+
25
+ This class contains static methods for token processing, agent identity resolution,
26
+ and other utility functions used across the Agent 365 runtime.
27
+ """
28
+
29
+ _cached_version = None
30
+
31
+ @staticmethod
32
+ def get_app_id_from_token(token: Optional[str]) -> str:
33
+ """
34
+ Decodes the current token and retrieves the App ID (appid or azp claim).
35
+
36
+ Args:
37
+ token: JWT token to decode. Can be None or empty.
38
+
39
+ Returns:
40
+ str: The App ID from the token's claims, or empty GUID if token is invalid.
41
+ Returns "00000000-0000-0000-0000-000000000000" if no valid App ID is found.
42
+ """
43
+ if not token or not token.strip():
44
+ return str(uuid.UUID(int=0))
45
+
46
+ try:
47
+ # Decode the JWT token without verification (we only need the claims)
48
+ # Note: verify=False is used because we only need to extract claims,
49
+ # not verify the token's authenticity
50
+ decoded_payload = jwt.decode(token, options={"verify_signature": False})
51
+
52
+ # Look for appid or azp claims (appid takes precedence)
53
+ app_id = decoded_payload.get("appid") or decoded_payload.get("azp")
54
+ return app_id if app_id else ""
55
+
56
+ except (jwt.DecodeError, jwt.InvalidTokenError):
57
+ # Token is malformed or invalid
58
+ return ""
59
+
60
+ @staticmethod
61
+ def resolve_agent_identity(context: Any, auth_token: Optional[str]) -> str:
62
+ """
63
+ Resolves the agent identity from the turn context or auth token.
64
+
65
+ Args:
66
+ context: Turn context of the conversation turn. Expected to have an Activity
67
+ with methods like is_agentic_request() and get_agentic_instance_id().
68
+ auth_token: Authentication token if available.
69
+
70
+ Returns:
71
+ str: The agent identity (App ID). Returns the agentic instance ID if the
72
+ request is agentic, otherwise returns the App ID from the auth token.
73
+ """
74
+ try:
75
+ # App ID is required to pass to MCP server URL
76
+ # Try to get agentic instance ID if this is an agentic request
77
+ if context and context.activity and context.activity.is_agentic_request():
78
+ agentic_id = context.activity.get_agentic_instance_id()
79
+ return agentic_id if agentic_id else ""
80
+
81
+ except (AttributeError, TypeError, Exception):
82
+ # Context/activity doesn't have the expected methods or properties
83
+ # or any other error occurred while accessing context/activity
84
+ pass
85
+
86
+ # Fallback to extracting App ID from the auth token
87
+ return Utility.get_app_id_from_token(auth_token)
88
+
89
+ @staticmethod
90
+ def get_user_agent_header(orchestrator: Optional[str] = None) -> str:
91
+ """
92
+ Generates a User-Agent header string for SDK requests.
93
+
94
+ Args:
95
+ orchestrator: Optional orchestrator name to include in the User-Agent header.
96
+ Defaults to empty string if not provided.
97
+
98
+ Returns:
99
+ str: A formatted User-Agent header string containing SDK version, OS type,
100
+ Python version, and optional orchestrator information.
101
+ """
102
+ if Utility._cached_version is None:
103
+ try:
104
+ Utility._cached_version = version("microsoft-agents-a365-runtime")
105
+ except PackageNotFoundError:
106
+ Utility._cached_version = "unknown"
107
+
108
+ orchestrator_part = f"; {orchestrator}" if orchestrator else ""
109
+ os_type = platform.system()
110
+ python_version = platform.python_version()
111
+ return f"Agent365SDK/{Utility._cached_version} ({os_type}; Python {python_version}{orchestrator_part})"
@@ -0,0 +1,34 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+
4
+ """
5
+ Version utilities for Microsoft Agent 365 SDK packages.
6
+
7
+ This module is deprecated. Versioning is now handled automatically by
8
+ setuptools-git-versioning. See versioning/TARGET-VERSION and
9
+ HOW_TO_SET_A_VERSION.md for details.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import os
15
+
16
+
17
+ def build_version():
18
+ """
19
+ DEPRECATED: This function is no longer used.
20
+
21
+ Version is now automatically calculated by setuptools-git-versioning
22
+ based on Git history and tags. See HOW_TO_SET_A_VERSION.md for details.
23
+
24
+ Returns:
25
+ str: Version from AGENT365_PYTHON_SDK_PACKAGE_VERSION environment variable or "0.0.0"
26
+ """
27
+ import warnings
28
+
29
+ warnings.warn(
30
+ "build_version() is deprecated. Version is now managed by setuptools-git-versioning.",
31
+ DeprecationWarning,
32
+ stacklevel=2,
33
+ )
34
+ return os.environ.get("AGENT365_PYTHON_SDK_PACKAGE_VERSION", "0.0.0")
@@ -0,0 +1,66 @@
1
+ Metadata-Version: 2.4
2
+ Name: microsoft-agents-a365-runtime
3
+ Version: 0.2.1.dev2
4
+ Summary: Telemetry, tracing, and monitoring components for AI agents
5
+ Author-email: Microsoft <support@microsoft.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/microsoft/Agent365-python
8
+ Project-URL: Repository, https://github.com/microsoft/Agent365-python
9
+ Project-URL: Issues, https://github.com/microsoft/Agent365-python/issues
10
+ Project-URL: Documentation, https://github.com/microsoft/Agent365-python/tree/main/libraries/microsoft-agents-a365-runtime
11
+ Keywords: observability,telemetry,tracing,opentelemetry,monitoring,ai,agents
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
20
+ Classifier: Topic :: System :: Monitoring
21
+ Requires-Python: >=3.11
22
+ Description-Content-Type: text/markdown
23
+ Requires-Dist: PyJWT>=2.8.0
24
+ Provides-Extra: dev
25
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
26
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
27
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
28
+ Requires-Dist: black>=23.0.0; extra == "dev"
29
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
30
+ Provides-Extra: test
31
+ Requires-Dist: pytest>=7.0.0; extra == "test"
32
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "test"
33
+
34
+ # microsoft-agents-a365-runtime
35
+
36
+ [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-a365-runtime?label=PyPI&logo=pypi)](https://pypi.org/project/microsoft-agents-a365-runtime)
37
+ [![PyPI Downloads](https://img.shields.io/pypi/dm/microsoft-agents-a365-runtime?label=Downloads&logo=pypi)](https://pypi.org/project/microsoft-agents-a365-runtime)
38
+
39
+ Core runtime utilities and environment management for AI agent applications. This package provides essential Power Platform API discovery, environment configuration, and authentication scope resolution.
40
+
41
+ ## Installation
42
+
43
+ ```bash
44
+ pip install microsoft-agents-a365-runtime
45
+ ```
46
+
47
+ ## Usage
48
+
49
+ For usage examples and detailed documentation, see the [Microsoft Agent 365 Developer documentation](https://learn.microsoft.com/microsoft-agent-365/developer/?tabs=python) on Microsoft Learn.
50
+
51
+ ## Support
52
+
53
+ For issues, questions, or feedback:
54
+
55
+ - File issues in the [GitHub Issues](https://github.com/microsoft/Agent365-python/issues) section
56
+ - See the [main documentation](../../README.md) for more information
57
+
58
+ ## Trademarks
59
+
60
+ *Microsoft, Windows, Microsoft Azure and/or other Microsoft products and services referenced in the documentation may be either trademarks or registered trademarks of Microsoft in the United States and/or other countries. The licenses for this project do not grant you rights to use any Microsoft names, logos, or trademarks. Microsoft's general trademark guidelines can be found at http://go.microsoft.com/fwlink/?LinkID=254653.*
61
+
62
+ ## License
63
+
64
+ Copyright (c) Microsoft Corporation. All rights reserved.
65
+
66
+ Licensed under the MIT License - see the [LICENSE](../../LICENSE.md) file for details.
@@ -0,0 +1,11 @@
1
+ microsoft_agents_a365/runtime/__init__.py,sha256=_Q6AfhdNV-YhvkJKqTG-TlImI2B5bEbtTC_Mv82-ob4,591
2
+ microsoft_agents_a365/runtime/environment_utils.py,sha256=B5A3CfGJJeuVgIW3Si7oSPYLQNkzc54nNqw3QnLgaC0,1824
3
+ microsoft_agents_a365/runtime/operation_error.py,sha256=OqG6IIQR5lfvZrtyqpTa9Gc4B_eo2qxfoJ1Cj96WiUs,1418
4
+ microsoft_agents_a365/runtime/operation_result.py,sha256=HIJLAzoAfPTINzjnThzXHDAizRaZ4xXX4D7TuOPjmdk,3066
5
+ microsoft_agents_a365/runtime/power_platform_api_discovery.py,sha256=B6Ar2-P9pHrv-Pe_vFl0j9gRqAWyipbTfxkLEc9aA0k,3460
6
+ microsoft_agents_a365/runtime/utility.py,sha256=5YEN1EGmHvRBb9gc9GeD58xAxUzGiZNON_zWkXh9Oms,4282
7
+ microsoft_agents_a365/runtime/version_utils.py,sha256=nkgYIaPXD9OyT_lJ8K3sqpEze_VMJzekMmjsiYNghB8,965
8
+ microsoft_agents_a365_runtime-0.2.1.dev2.dist-info/METADATA,sha256=CUvTC5htAC9pCUy4IktF7y0LIIlHt6RPXy1vis9zxuw,3205
9
+ microsoft_agents_a365_runtime-0.2.1.dev2.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
10
+ microsoft_agents_a365_runtime-0.2.1.dev2.dist-info/top_level.txt,sha256=G3c2_4sy5_EM_BWO67SbK2tKj4G8XFn-QXRbh8g9Lgk,22
11
+ microsoft_agents_a365_runtime-0.2.1.dev2.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.10.2)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ microsoft_agents_a365