From a91be2bdd84ec80d81b2215af88014c601f25905 Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Mon, 4 May 2026 13:50:37 -0700 Subject: [PATCH 01/15] Make microsoft-agents-hosting-core and microsoft-agents-activity optional dependencies --- MIGRATION_A365.md | 10 ++++++++++ pyproject.toml | 6 ++++-- src/microsoft/opentelemetry/a365/constants.py | 7 +++++++ .../a365/hosting/middleware/baggage_middleware.py | 12 ++++++++++-- .../middleware/observability_hosting_manager.py | 7 ++++++- .../hosting/middleware/output_logging_middleware.py | 10 ++++++++-- .../a365/hosting/scope_helpers/populate_baggage.py | 10 +++++++++- .../a365/hosting/scope_helpers/utils.py | 10 +++++++++- .../hosting/token_cache_helpers/agent_token_cache.py | 9 +++++++-- 9 files changed, 70 insertions(+), 11 deletions(-) diff --git a/MIGRATION_A365.md b/MIGRATION_A365.md index 9576283c..edd47ef8 100644 --- a/MIGRATION_A365.md +++ b/MIGRATION_A365.md @@ -36,8 +36,18 @@ pip uninstall -y microsoft-agents-a365-observability-extensions-agent-framework # ✅ Install the new single package pip install microsoft-opentelemetry + +# If you use the hosting middleware (BaggageMiddleware, +# ObservabilityHostingManager, etc.), also install the hosting extra: +pip install "microsoft-opentelemetry[hosting]" ``` +> The `microsoft-agents-activity` and `microsoft-agents-hosting-core` +> packages are now **optional** dependencies pulled in by the `[hosting]` +> extra. Without them, importing `microsoft.opentelemetry.a365.hosting` +> succeeds silently but accessing any symbol from it raises `ImportError` +> with an install hint. + ## Step 2 — Rewrite Import Paths The old packages used `microsoft_agents_a365.*` namespace. diff --git a/pyproject.toml b/pyproject.toml index 77dcf390..30e6970b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,14 +41,16 @@ dependencies = [ "opentelemetry-resource-detector-azure<1.0.0,>=0.1.5", "wrapt>=1.0", "opentelemetry-util-genai>=0.3b0", - "microsoft-agents-activity>=0.9.0", - "microsoft-agents-hosting-core>=0.9.0", "aiohttp>=3.8.0", "PyJWT", "requests" ] [project.optional-dependencies] +hosting = [ + "microsoft-agents-activity>=0.9.0", + "microsoft-agents-hosting-core>=0.9.0", +] langchain = [ "langchain-core>=0.2.0", ] diff --git a/src/microsoft/opentelemetry/a365/constants.py b/src/microsoft/opentelemetry/a365/constants.py index f02c03f5..2f00a614 100644 --- a/src/microsoft/opentelemetry/a365/constants.py +++ b/src/microsoft/opentelemetry/a365/constants.py @@ -132,3 +132,10 @@ A365_SERVICE_CLIENT_ID_ENV = "CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTID" A365_SERVICE_CLIENT_SECRET_ENV = "CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTSECRET" A365_SERVICE_TENANT_ID_ENV = "CONNECTIONS__SERVICE_CONNECTION__SETTINGS__TENANTID" + +# --- Optional dependency install hints --- +HOSTING_INSTALL_HINT = ( + "microsoft.opentelemetry.a365.hosting requires 'microsoft-agents-activity>=0.9.0' " + "and 'microsoft-agents-hosting-core>=0.9.0'. Install them with: " + 'pip install "microsoft-opentelemetry[hosting]"' +) \ No newline at end of file diff --git a/src/microsoft/opentelemetry/a365/hosting/middleware/baggage_middleware.py b/src/microsoft/opentelemetry/a365/hosting/middleware/baggage_middleware.py index d2f469e8..14a4d0cf 100644 --- a/src/microsoft/opentelemetry/a365/hosting/middleware/baggage_middleware.py +++ b/src/microsoft/opentelemetry/a365/hosting/middleware/baggage_middleware.py @@ -7,8 +7,16 @@ from collections.abc import Awaitable, Callable -from microsoft_agents.activity import ActivityEventNames, ActivityTypes -from microsoft_agents.hosting.core.turn_context import TurnContext +try: + from microsoft_agents.activity import ActivityEventNames, ActivityTypes + from microsoft_agents.hosting.core.turn_context import TurnContext +except ImportError: # pragma: no cover - optional dependency + import logging as _logging + + from microsoft.opentelemetry.a365.constants import HOSTING_INSTALL_HINT + + _logging.getLogger(__name__).warning(HOSTING_INSTALL_HINT) + from microsoft.opentelemetry.a365.core.middleware.baggage_builder import BaggageBuilder from microsoft.opentelemetry.a365.hosting.scope_helpers.populate_baggage import populate diff --git a/src/microsoft/opentelemetry/a365/hosting/middleware/observability_hosting_manager.py b/src/microsoft/opentelemetry/a365/hosting/middleware/observability_hosting_manager.py index 0c8f9885..fcc85a96 100644 --- a/src/microsoft/opentelemetry/a365/hosting/middleware/observability_hosting_manager.py +++ b/src/microsoft/opentelemetry/a365/hosting/middleware/observability_hosting_manager.py @@ -8,7 +8,12 @@ import logging from dataclasses import dataclass -from microsoft_agents.hosting.core.middleware_set import MiddlewareSet +try: + from microsoft_agents.hosting.core.middleware_set import MiddlewareSet +except ImportError: # pragma: no cover - optional dependency + from microsoft.opentelemetry.a365.constants import HOSTING_INSTALL_HINT + + logging.getLogger(__name__).warning(HOSTING_INSTALL_HINT) from microsoft.opentelemetry.a365.hosting.middleware.baggage_middleware import BaggageMiddleware from microsoft.opentelemetry.a365.hosting.middleware.output_logging_middleware import OutputLoggingMiddleware diff --git a/src/microsoft/opentelemetry/a365/hosting/middleware/output_logging_middleware.py b/src/microsoft/opentelemetry/a365/hosting/middleware/output_logging_middleware.py index 34411d9a..fd2a30d5 100644 --- a/src/microsoft/opentelemetry/a365/hosting/middleware/output_logging_middleware.py +++ b/src/microsoft/opentelemetry/a365/hosting/middleware/output_logging_middleware.py @@ -8,8 +8,14 @@ import logging from collections.abc import Awaitable, Callable -from microsoft_agents.activity import Activity -from microsoft_agents.hosting.core.turn_context import TurnContext +try: + from microsoft_agents.activity import Activity + from microsoft_agents.hosting.core.turn_context import TurnContext +except ImportError: # pragma: no cover - optional dependency + from microsoft.opentelemetry.a365.constants import HOSTING_INSTALL_HINT + + logging.getLogger(__name__).warning(HOSTING_INSTALL_HINT) + from microsoft.opentelemetry.a365.core.agent_details import AgentDetails from microsoft.opentelemetry.a365.constants import ( CHANNEL_LINK_KEY, diff --git a/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_baggage.py b/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_baggage.py index f0e95bf1..048d4f8b 100644 --- a/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_baggage.py +++ b/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_baggage.py @@ -6,7 +6,15 @@ from collections.abc import Iterator from typing import Any -from microsoft_agents.hosting.core.turn_context import TurnContext +try: + from microsoft_agents.hosting.core.turn_context import TurnContext +except ImportError: # pragma: no cover - optional dependency + import logging as _logging + + from microsoft.opentelemetry.a365.constants import HOSTING_INSTALL_HINT + + _logging.getLogger(__name__).warning(HOSTING_INSTALL_HINT) + from microsoft.opentelemetry.a365.core.middleware.baggage_builder import BaggageBuilder from microsoft.opentelemetry.a365.hosting.scope_helpers.utils import ( diff --git a/src/microsoft/opentelemetry/a365/hosting/scope_helpers/utils.py b/src/microsoft/opentelemetry/a365/hosting/scope_helpers/utils.py index a02da39d..bc7896de 100644 --- a/src/microsoft/opentelemetry/a365/hosting/scope_helpers/utils.py +++ b/src/microsoft/opentelemetry/a365/hosting/scope_helpers/utils.py @@ -4,7 +4,15 @@ from collections.abc import Iterator from typing import Any -from microsoft_agents.activity import Activity +try: + from microsoft_agents.activity import Activity +except ImportError: # pragma: no cover - optional dependency + import logging as _logging + + from microsoft.opentelemetry.a365.constants import HOSTING_INSTALL_HINT + + _logging.getLogger(__name__).warning(HOSTING_INSTALL_HINT) + from microsoft.opentelemetry.a365.constants import ( CHANNEL_LINK_KEY, CHANNEL_NAME_KEY, diff --git a/src/microsoft/opentelemetry/a365/hosting/token_cache_helpers/agent_token_cache.py b/src/microsoft/opentelemetry/a365/hosting/token_cache_helpers/agent_token_cache.py index 13ad2bae..66e20efe 100644 --- a/src/microsoft/opentelemetry/a365/hosting/token_cache_helpers/agent_token_cache.py +++ b/src/microsoft/opentelemetry/a365/hosting/token_cache_helpers/agent_token_cache.py @@ -11,8 +11,13 @@ from dataclasses import dataclass from threading import Lock -from microsoft_agents.hosting.core.app.oauth.authorization import Authorization -from microsoft_agents.hosting.core.turn_context import TurnContext +try: + from microsoft_agents.hosting.core.app.oauth.authorization import Authorization + from microsoft_agents.hosting.core.turn_context import TurnContext +except ImportError: # pragma: no cover - optional dependency + from microsoft.opentelemetry.a365.constants import HOSTING_INSTALL_HINT + + logging.getLogger(__name__).warning(HOSTING_INSTALL_HINT) logger = logging.getLogger(__name__) From 722a3104a15f02572caec76dea12c4f5b6bf45f3 Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Mon, 4 May 2026 14:24:27 -0700 Subject: [PATCH 02/15] Adjust tests --- tests/a365/hosting/middleware/test_baggage_middleware.py | 5 +++++ .../middleware/test_observability_hosting_manager.py | 5 +++++ .../hosting/middleware/test_output_logging_middleware.py | 5 +++++ tests/a365/hosting/scope_helpers/test_populate_baggage.py | 7 ++++++- .../scope_helpers/test_populate_invoke_agent_scope.py | 5 +++++ .../a365/hosting/scope_helpers/test_scope_helper_utils.py | 5 +++++ .../hosting/token_cache_helpers/test_agent_token_cache.py | 4 ++++ 7 files changed, 35 insertions(+), 1 deletion(-) diff --git a/tests/a365/hosting/middleware/test_baggage_middleware.py b/tests/a365/hosting/middleware/test_baggage_middleware.py index 04d38864..d4e3a6b0 100644 --- a/tests/a365/hosting/middleware/test_baggage_middleware.py +++ b/tests/a365/hosting/middleware/test_baggage_middleware.py @@ -4,6 +4,11 @@ from unittest.mock import MagicMock import pytest + +pytest.importorskip("microsoft_agents.activity") +pytest.importorskip("microsoft_agents.hosting.core") + +# pylint: disable=wrong-import-position from microsoft_agents.activity import ( Activity, ActivityEventNames, diff --git a/tests/a365/hosting/middleware/test_observability_hosting_manager.py b/tests/a365/hosting/middleware/test_observability_hosting_manager.py index 1c3c200e..dac0e2ae 100644 --- a/tests/a365/hosting/middleware/test_observability_hosting_manager.py +++ b/tests/a365/hosting/middleware/test_observability_hosting_manager.py @@ -4,6 +4,11 @@ from unittest.mock import MagicMock import pytest + +pytest.importorskip("microsoft_agents.activity") +pytest.importorskip("microsoft_agents.hosting.core") + +# pylint: disable=wrong-import-position from microsoft.opentelemetry.a365.hosting.middleware.baggage_middleware import ( BaggageMiddleware, ) diff --git a/tests/a365/hosting/middleware/test_output_logging_middleware.py b/tests/a365/hosting/middleware/test_output_logging_middleware.py index 58805a94..fbeabf8d 100644 --- a/tests/a365/hosting/middleware/test_output_logging_middleware.py +++ b/tests/a365/hosting/middleware/test_output_logging_middleware.py @@ -4,6 +4,11 @@ from unittest.mock import AsyncMock, MagicMock, patch import pytest + +pytest.importorskip("microsoft_agents.activity") +pytest.importorskip("microsoft_agents.hosting.core") + +# pylint: disable=wrong-import-position from microsoft_agents.activity import ( Activity, ChannelAccount, diff --git a/tests/a365/hosting/scope_helpers/test_populate_baggage.py b/tests/a365/hosting/scope_helpers/test_populate_baggage.py index 4e90f0c4..e8a1af51 100644 --- a/tests/a365/hosting/scope_helpers/test_populate_baggage.py +++ b/tests/a365/hosting/scope_helpers/test_populate_baggage.py @@ -3,13 +3,18 @@ from unittest.mock import MagicMock +import pytest + +pytest.importorskip("microsoft_agents.activity") +pytest.importorskip("microsoft_agents.hosting.core") + +# pylint: disable=wrong-import-position from microsoft_agents.activity import Activity, ChannelAccount, ConversationAccount from microsoft_agents.hosting.core import TurnContext from microsoft.opentelemetry.a365.core.constants import USER_ID_KEY from microsoft.opentelemetry.a365.core.middleware.baggage_builder import BaggageBuilder from microsoft.opentelemetry.a365.hosting.scope_helpers.populate_baggage import populate - def test_populate(): """Test populate populates BaggageBuilder from turn context.""" # Create a real activity and turn context diff --git a/tests/a365/hosting/scope_helpers/test_populate_invoke_agent_scope.py b/tests/a365/hosting/scope_helpers/test_populate_invoke_agent_scope.py index 102843cc..2a70f852 100644 --- a/tests/a365/hosting/scope_helpers/test_populate_invoke_agent_scope.py +++ b/tests/a365/hosting/scope_helpers/test_populate_invoke_agent_scope.py @@ -5,6 +5,11 @@ from unittest.mock import MagicMock import pytest + +pytest.importorskip("microsoft_agents.activity") +pytest.importorskip("microsoft_agents.hosting.core") + +# pylint: disable=wrong-import-position from microsoft_agents.activity import Activity, ChannelAccount, ConversationAccount from microsoft_agents.hosting.core import TurnContext from microsoft.opentelemetry.a365.core.agent_details import AgentDetails diff --git a/tests/a365/hosting/scope_helpers/test_scope_helper_utils.py b/tests/a365/hosting/scope_helpers/test_scope_helper_utils.py index 8d0ee845..d4582aee 100644 --- a/tests/a365/hosting/scope_helpers/test_scope_helper_utils.py +++ b/tests/a365/hosting/scope_helpers/test_scope_helper_utils.py @@ -1,6 +1,11 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. +import pytest + +pytest.importorskip("microsoft_agents.activity") + +# pylint: disable=wrong-import-position from microsoft_agents.activity import Activity, ChannelAccount, ConversationAccount from microsoft.opentelemetry.a365.core.constants import ( CHANNEL_LINK_KEY, diff --git a/tests/a365/hosting/token_cache_helpers/test_agent_token_cache.py b/tests/a365/hosting/token_cache_helpers/test_agent_token_cache.py index 90130699..28244a15 100644 --- a/tests/a365/hosting/token_cache_helpers/test_agent_token_cache.py +++ b/tests/a365/hosting/token_cache_helpers/test_agent_token_cache.py @@ -7,6 +7,10 @@ from unittest.mock import AsyncMock, MagicMock import pytest + +pytest.importorskip("microsoft_agents.hosting.core") + +# pylint: disable=wrong-import-position from microsoft_agents.hosting.core.app.oauth.authorization import Authorization from microsoft_agents.hosting.core.turn_context import TurnContext from microsoft.opentelemetry.a365.hosting.token_cache_helpers import ( From 3bf5865d9d3f02fa6fcec94b39a419cc13df03e9 Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Mon, 4 May 2026 14:54:54 -0700 Subject: [PATCH 03/15] Address comments --- MIGRATION_A365.md | 8 +++-- src/microsoft/opentelemetry/a365/constants.py | 2 +- .../hosting/middleware/baggage_middleware.py | 24 +++++++++------ .../observability_hosting_manager.py | 21 +++++++++----- .../middleware/output_logging_middleware.py | 25 ++++++++++------ .../hosting/scope_helpers/populate_baggage.py | 26 ++++++++++------- .../a365/hosting/scope_helpers/utils.py | 29 ++++++++++++------- .../token_cache_helpers/agent_token_cache.py | 16 ++++++---- 8 files changed, 96 insertions(+), 55 deletions(-) diff --git a/MIGRATION_A365.md b/MIGRATION_A365.md index edd47ef8..526089bd 100644 --- a/MIGRATION_A365.md +++ b/MIGRATION_A365.md @@ -45,8 +45,12 @@ pip install "microsoft-opentelemetry[hosting]" > The `microsoft-agents-activity` and `microsoft-agents-hosting-core` > packages are now **optional** dependencies pulled in by the `[hosting]` > extra. Without them, importing `microsoft.opentelemetry.a365.hosting` -> succeeds silently but accessing any symbol from it raises `ImportError` -> with an install hint. +> logs a warning with an install hint and continues — the optional +> classes/types are stubbed to ``None`` so the module import does not +> crash. Actually instantiating or calling the hosting middleware +> (`BaggageMiddleware`, `ObservabilityHostingManager`, +> `OutputLoggingMiddleware`, `AgenticTokenCache`, etc.) will then fail +> at runtime, so install the `[hosting]` extra before relying on it. ## Step 2 — Rewrite Import Paths diff --git a/src/microsoft/opentelemetry/a365/constants.py b/src/microsoft/opentelemetry/a365/constants.py index 2f00a614..dfe57097 100644 --- a/src/microsoft/opentelemetry/a365/constants.py +++ b/src/microsoft/opentelemetry/a365/constants.py @@ -138,4 +138,4 @@ "microsoft.opentelemetry.a365.hosting requires 'microsoft-agents-activity>=0.9.0' " "and 'microsoft-agents-hosting-core>=0.9.0'. Install them with: " 'pip install "microsoft-opentelemetry[hosting]"' -) \ No newline at end of file +) diff --git a/src/microsoft/opentelemetry/a365/hosting/middleware/baggage_middleware.py b/src/microsoft/opentelemetry/a365/hosting/middleware/baggage_middleware.py index 14a4d0cf..bb0efde0 100644 --- a/src/microsoft/opentelemetry/a365/hosting/middleware/baggage_middleware.py +++ b/src/microsoft/opentelemetry/a365/hosting/middleware/baggage_middleware.py @@ -6,20 +6,26 @@ from __future__ import annotations from collections.abc import Awaitable, Callable +from microsoft.opentelemetry.a365.core.middleware.baggage_builder import BaggageBuilder -try: - from microsoft_agents.activity import ActivityEventNames, ActivityTypes - from microsoft_agents.hosting.core.turn_context import TurnContext -except ImportError: # pragma: no cover - optional dependency - import logging as _logging +from microsoft.opentelemetry.a365.hosting.scope_helpers.populate_baggage import populate - from microsoft.opentelemetry.a365.constants import HOSTING_INSTALL_HINT +from typing import TYPE_CHECKING - _logging.getLogger(__name__).warning(HOSTING_INSTALL_HINT) +if TYPE_CHECKING: + from microsoft_agents.activity import ActivityEventNames, ActivityTypes + from microsoft_agents.hosting.core.turn_context import TurnContext +else: # pyright: ignore[reportUnreachable] + try: + from microsoft_agents.activity import ActivityEventNames, ActivityTypes + from microsoft_agents.hosting.core.turn_context import TurnContext + except ImportError: # pragma: no cover - optional dependency + import logging as _logging -from microsoft.opentelemetry.a365.core.middleware.baggage_builder import BaggageBuilder + from microsoft.opentelemetry.a365.constants import HOSTING_INSTALL_HINT -from microsoft.opentelemetry.a365.hosting.scope_helpers.populate_baggage import populate + _logging.getLogger(__name__).warning(HOSTING_INSTALL_HINT) + ActivityEventNames = ActivityTypes = TurnContext = None # mypy: disable-error-code="call-arg" diff --git a/src/microsoft/opentelemetry/a365/hosting/middleware/observability_hosting_manager.py b/src/microsoft/opentelemetry/a365/hosting/middleware/observability_hosting_manager.py index fcc85a96..072faa9b 100644 --- a/src/microsoft/opentelemetry/a365/hosting/middleware/observability_hosting_manager.py +++ b/src/microsoft/opentelemetry/a365/hosting/middleware/observability_hosting_manager.py @@ -7,17 +7,22 @@ import logging from dataclasses import dataclass - -try: - from microsoft_agents.hosting.core.middleware_set import MiddlewareSet -except ImportError: # pragma: no cover - optional dependency - from microsoft.opentelemetry.a365.constants import HOSTING_INSTALL_HINT - - logging.getLogger(__name__).warning(HOSTING_INSTALL_HINT) - from microsoft.opentelemetry.a365.hosting.middleware.baggage_middleware import BaggageMiddleware from microsoft.opentelemetry.a365.hosting.middleware.output_logging_middleware import OutputLoggingMiddleware +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from microsoft_agents.hosting.core.middleware_set import MiddlewareSet +else: # pyright: ignore[reportUnreachable] + try: + from microsoft_agents.hosting.core.middleware_set import MiddlewareSet + except ImportError: # pragma: no cover - optional dependency + from microsoft.opentelemetry.a365.constants import HOSTING_INSTALL_HINT + + logging.getLogger(__name__).warning(HOSTING_INSTALL_HINT) + MiddlewareSet = None + logger = logging.getLogger(__name__) diff --git a/src/microsoft/opentelemetry/a365/hosting/middleware/output_logging_middleware.py b/src/microsoft/opentelemetry/a365/hosting/middleware/output_logging_middleware.py index fd2a30d5..03d9851f 100644 --- a/src/microsoft/opentelemetry/a365/hosting/middleware/output_logging_middleware.py +++ b/src/microsoft/opentelemetry/a365/hosting/middleware/output_logging_middleware.py @@ -7,15 +7,6 @@ import logging from collections.abc import Awaitable, Callable - -try: - from microsoft_agents.activity import Activity - from microsoft_agents.hosting.core.turn_context import TurnContext -except ImportError: # pragma: no cover - optional dependency - from microsoft.opentelemetry.a365.constants import HOSTING_INSTALL_HINT - - logging.getLogger(__name__).warning(HOSTING_INSTALL_HINT) - from microsoft.opentelemetry.a365.core.agent_details import AgentDetails from microsoft.opentelemetry.a365.constants import ( CHANNEL_LINK_KEY, @@ -28,6 +19,22 @@ from microsoft.opentelemetry.a365.core.spans_scopes.output_scope import OutputScope from microsoft.opentelemetry.a365.core.utils import extract_context_from_headers +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from microsoft_agents.activity import Activity + from microsoft_agents.hosting.core.turn_context import TurnContext +else: # pyright: ignore[reportUnreachable] + try: + from microsoft_agents.activity import Activity + from microsoft_agents.hosting.core.turn_context import TurnContext + except ImportError: # pragma: no cover - optional dependency + from microsoft.opentelemetry.a365.constants import HOSTING_INSTALL_HINT + + logging.getLogger(__name__).warning(HOSTING_INSTALL_HINT) + Activity = TurnContext = None + + # mypy: disable-error-code="call-arg" logger = logging.getLogger(__name__) diff --git a/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_baggage.py b/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_baggage.py index 048d4f8b..bfc420dd 100644 --- a/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_baggage.py +++ b/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_baggage.py @@ -4,16 +4,6 @@ from __future__ import annotations from collections.abc import Iterator -from typing import Any - -try: - from microsoft_agents.hosting.core.turn_context import TurnContext -except ImportError: # pragma: no cover - optional dependency - import logging as _logging - - from microsoft.opentelemetry.a365.constants import HOSTING_INSTALL_HINT - - _logging.getLogger(__name__).warning(HOSTING_INSTALL_HINT) from microsoft.opentelemetry.a365.core.middleware.baggage_builder import BaggageBuilder @@ -24,6 +14,22 @@ get_target_agent_pairs, get_tenant_id_pair, ) +from typing import TYPE_CHECKING, Any + +if TYPE_CHECKING: + from microsoft_agents.hosting.core.turn_context import TurnContext +else: # pyright: ignore[reportUnreachable] + try: + from microsoft_agents.hosting.core.turn_context import TurnContext + except ImportError: # pragma: no cover - optional dependency + import logging as _logging + + from microsoft.opentelemetry.a365.constants import HOSTING_INSTALL_HINT + + _logging.getLogger(__name__).warning(HOSTING_INSTALL_HINT) + TurnContext = None + + def _iter_all_pairs(turn_context: TurnContext) -> Iterator[tuple[str, Any]]: diff --git a/src/microsoft/opentelemetry/a365/hosting/scope_helpers/utils.py b/src/microsoft/opentelemetry/a365/hosting/scope_helpers/utils.py index bc7896de..39cd2a51 100644 --- a/src/microsoft/opentelemetry/a365/hosting/scope_helpers/utils.py +++ b/src/microsoft/opentelemetry/a365/hosting/scope_helpers/utils.py @@ -1,18 +1,9 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. -from collections.abc import Iterator -from typing import Any - -try: - from microsoft_agents.activity import Activity -except ImportError: # pragma: no cover - optional dependency - import logging as _logging - - from microsoft.opentelemetry.a365.constants import HOSTING_INSTALL_HINT - - _logging.getLogger(__name__).warning(HOSTING_INSTALL_HINT) +from __future__ import annotations +from collections.abc import Iterator from microsoft.opentelemetry.a365.constants import ( CHANNEL_LINK_KEY, CHANNEL_NAME_KEY, @@ -28,6 +19,22 @@ USER_ID_KEY, USER_NAME_KEY, ) +from typing import TYPE_CHECKING, Any + +if TYPE_CHECKING: + from microsoft_agents.activity import Activity +else: # pyright: ignore[reportUnreachable] + try: + from microsoft_agents.activity import Activity + except ImportError: # pragma: no cover - optional dependency + import logging as _logging + + from microsoft.opentelemetry.a365.constants import HOSTING_INSTALL_HINT + + _logging.getLogger(__name__).warning(HOSTING_INSTALL_HINT) + Activity = None + + AGENT_ROLE = "agenticUser" diff --git a/src/microsoft/opentelemetry/a365/hosting/token_cache_helpers/agent_token_cache.py b/src/microsoft/opentelemetry/a365/hosting/token_cache_helpers/agent_token_cache.py index 66e20efe..db10ab7f 100644 --- a/src/microsoft/opentelemetry/a365/hosting/token_cache_helpers/agent_token_cache.py +++ b/src/microsoft/opentelemetry/a365/hosting/token_cache_helpers/agent_token_cache.py @@ -10,14 +10,20 @@ import logging from dataclasses import dataclass from threading import Lock +from typing import TYPE_CHECKING -try: +if TYPE_CHECKING: from microsoft_agents.hosting.core.app.oauth.authorization import Authorization from microsoft_agents.hosting.core.turn_context import TurnContext -except ImportError: # pragma: no cover - optional dependency - from microsoft.opentelemetry.a365.constants import HOSTING_INSTALL_HINT - - logging.getLogger(__name__).warning(HOSTING_INSTALL_HINT) +else: # pyright: ignore[reportUnreachable] + try: + from microsoft_agents.hosting.core.app.oauth.authorization import Authorization + from microsoft_agents.hosting.core.turn_context import TurnContext + except ImportError: # pragma: no cover - optional dependency + from microsoft.opentelemetry.a365.constants import HOSTING_INSTALL_HINT + + logging.getLogger(__name__).warning(HOSTING_INSTALL_HINT) + Authorization = TurnContext = None logger = logging.getLogger(__name__) From a31d335b503b02468782f0cf6a71b41355a6bd80 Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Mon, 4 May 2026 15:20:37 -0700 Subject: [PATCH 04/15] Address comments --- .../hosting/middleware/baggage_middleware.py | 20 ++++++++++++++----- .../observability_hosting_manager.py | 14 ++++++++++--- .../middleware/output_logging_middleware.py | 16 ++++++++++++--- .../hosting/scope_helpers/populate_baggage.py | 7 ++----- .../a365/hosting/scope_helpers/utils.py | 7 ++----- .../token_cache_helpers/agent_token_cache.py | 14 ++++++++++--- 6 files changed, 54 insertions(+), 24 deletions(-) diff --git a/src/microsoft/opentelemetry/a365/hosting/middleware/baggage_middleware.py b/src/microsoft/opentelemetry/a365/hosting/middleware/baggage_middleware.py index bb0efde0..28bb68d3 100644 --- a/src/microsoft/opentelemetry/a365/hosting/middleware/baggage_middleware.py +++ b/src/microsoft/opentelemetry/a365/hosting/middleware/baggage_middleware.py @@ -5,7 +5,9 @@ from __future__ import annotations +import logging from collections.abc import Awaitable, Callable +from microsoft.opentelemetry.a365.constants import HOSTING_INSTALL_HINT from microsoft.opentelemetry.a365.core.middleware.baggage_builder import BaggageBuilder from microsoft.opentelemetry.a365.hosting.scope_helpers.populate_baggage import populate @@ -15,20 +17,24 @@ if TYPE_CHECKING: from microsoft_agents.activity import ActivityEventNames, ActivityTypes from microsoft_agents.hosting.core.turn_context import TurnContext + + _HOSTING_AVAILABLE = True else: # pyright: ignore[reportUnreachable] try: from microsoft_agents.activity import ActivityEventNames, ActivityTypes from microsoft_agents.hosting.core.turn_context import TurnContext - except ImportError: # pragma: no cover - optional dependency - import logging as _logging - - from microsoft.opentelemetry.a365.constants import HOSTING_INSTALL_HINT - _logging.getLogger(__name__).warning(HOSTING_INSTALL_HINT) + _HOSTING_AVAILABLE = True + except ImportError: # pragma: no cover - optional dependency + # Stub silently; the warning is emitted in __init__ when the user + # actually instantiates the middleware. ActivityEventNames = ActivityTypes = TurnContext = None + _HOSTING_AVAILABLE = False # mypy: disable-error-code="call-arg" +_logger = logging.getLogger(__name__) + class BaggageMiddleware: """Middleware that propagates OpenTelemetry baggage context derived from TurnContext. @@ -36,6 +42,10 @@ class BaggageMiddleware: Async replies (ContinueConversation) are passed through without baggage setup. """ + def __init__(self) -> None: + if not _HOSTING_AVAILABLE: + _logger.warning(HOSTING_INSTALL_HINT) + async def on_turn( self, context: TurnContext, diff --git a/src/microsoft/opentelemetry/a365/hosting/middleware/observability_hosting_manager.py b/src/microsoft/opentelemetry/a365/hosting/middleware/observability_hosting_manager.py index 072faa9b..0f8f9049 100644 --- a/src/microsoft/opentelemetry/a365/hosting/middleware/observability_hosting_manager.py +++ b/src/microsoft/opentelemetry/a365/hosting/middleware/observability_hosting_manager.py @@ -10,18 +10,24 @@ from microsoft.opentelemetry.a365.hosting.middleware.baggage_middleware import BaggageMiddleware from microsoft.opentelemetry.a365.hosting.middleware.output_logging_middleware import OutputLoggingMiddleware +from microsoft.opentelemetry.a365.constants import HOSTING_INSTALL_HINT + from typing import TYPE_CHECKING if TYPE_CHECKING: from microsoft_agents.hosting.core.middleware_set import MiddlewareSet + + _HOSTING_AVAILABLE = True else: # pyright: ignore[reportUnreachable] try: from microsoft_agents.hosting.core.middleware_set import MiddlewareSet - except ImportError: # pragma: no cover - optional dependency - from microsoft.opentelemetry.a365.constants import HOSTING_INSTALL_HINT - logging.getLogger(__name__).warning(HOSTING_INSTALL_HINT) + _HOSTING_AVAILABLE = True + except ImportError: # pragma: no cover - optional dependency + # Stub silently; the warning is emitted in configure() when the user + # actually attempts to wire up hosting middleware. MiddlewareSet = None + _HOSTING_AVAILABLE = False logger = logging.getLogger(__name__) @@ -74,6 +80,8 @@ def configure( Raises: TypeError: If *middleware_set* or *options* is ``None``. """ + if not _HOSTING_AVAILABLE: + logger.warning(HOSTING_INSTALL_HINT) if middleware_set is None: raise TypeError("middleware_set must not be None") if options is None: diff --git a/src/microsoft/opentelemetry/a365/hosting/middleware/output_logging_middleware.py b/src/microsoft/opentelemetry/a365/hosting/middleware/output_logging_middleware.py index 03d9851f..347b4112 100644 --- a/src/microsoft/opentelemetry/a365/hosting/middleware/output_logging_middleware.py +++ b/src/microsoft/opentelemetry/a365/hosting/middleware/output_logging_middleware.py @@ -19,20 +19,26 @@ from microsoft.opentelemetry.a365.core.spans_scopes.output_scope import OutputScope from microsoft.opentelemetry.a365.core.utils import extract_context_from_headers +from microsoft.opentelemetry.a365.constants import HOSTING_INSTALL_HINT + from typing import TYPE_CHECKING if TYPE_CHECKING: from microsoft_agents.activity import Activity from microsoft_agents.hosting.core.turn_context import TurnContext + + _HOSTING_AVAILABLE = True else: # pyright: ignore[reportUnreachable] try: from microsoft_agents.activity import Activity from microsoft_agents.hosting.core.turn_context import TurnContext - except ImportError: # pragma: no cover - optional dependency - from microsoft.opentelemetry.a365.constants import HOSTING_INSTALL_HINT - logging.getLogger(__name__).warning(HOSTING_INSTALL_HINT) + _HOSTING_AVAILABLE = True + except ImportError: # pragma: no cover - optional dependency + # Stub silently; the warning is emitted in __init__ when the user + # actually instantiates the middleware. Activity = TurnContext = None + _HOSTING_AVAILABLE = False # mypy: disable-error-code="call-arg" @@ -109,6 +115,10 @@ class OutputLoggingMiddleware: attributes and exported to the configured telemetry backend. """ + def __init__(self) -> None: + if not _HOSTING_AVAILABLE: + logger.warning(HOSTING_INSTALL_HINT) + async def on_turn( self, context: TurnContext, diff --git a/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_baggage.py b/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_baggage.py index bfc420dd..f2e7908e 100644 --- a/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_baggage.py +++ b/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_baggage.py @@ -22,11 +22,8 @@ try: from microsoft_agents.hosting.core.turn_context import TurnContext except ImportError: # pragma: no cover - optional dependency - import logging as _logging - - from microsoft.opentelemetry.a365.constants import HOSTING_INSTALL_HINT - - _logging.getLogger(__name__).warning(HOSTING_INSTALL_HINT) + # Stub silently; warning is emitted by the user-facing entry-point + # (middleware/cache constructors) via HOSTING_INSTALL_HINT. TurnContext = None diff --git a/src/microsoft/opentelemetry/a365/hosting/scope_helpers/utils.py b/src/microsoft/opentelemetry/a365/hosting/scope_helpers/utils.py index 39cd2a51..1f23468f 100644 --- a/src/microsoft/opentelemetry/a365/hosting/scope_helpers/utils.py +++ b/src/microsoft/opentelemetry/a365/hosting/scope_helpers/utils.py @@ -27,11 +27,8 @@ try: from microsoft_agents.activity import Activity except ImportError: # pragma: no cover - optional dependency - import logging as _logging - - from microsoft.opentelemetry.a365.constants import HOSTING_INSTALL_HINT - - _logging.getLogger(__name__).warning(HOSTING_INSTALL_HINT) + # Stub silently; warning is emitted by the user-facing entry-point + # (middleware/cache constructors) via HOSTING_INSTALL_HINT. Activity = None diff --git a/src/microsoft/opentelemetry/a365/hosting/token_cache_helpers/agent_token_cache.py b/src/microsoft/opentelemetry/a365/hosting/token_cache_helpers/agent_token_cache.py index db10ab7f..d63f0fe6 100644 --- a/src/microsoft/opentelemetry/a365/hosting/token_cache_helpers/agent_token_cache.py +++ b/src/microsoft/opentelemetry/a365/hosting/token_cache_helpers/agent_token_cache.py @@ -12,18 +12,24 @@ from threading import Lock from typing import TYPE_CHECKING +from microsoft.opentelemetry.a365.constants import HOSTING_INSTALL_HINT + if TYPE_CHECKING: from microsoft_agents.hosting.core.app.oauth.authorization import Authorization from microsoft_agents.hosting.core.turn_context import TurnContext + + _HOSTING_AVAILABLE = True else: # pyright: ignore[reportUnreachable] try: from microsoft_agents.hosting.core.app.oauth.authorization import Authorization from microsoft_agents.hosting.core.turn_context import TurnContext - except ImportError: # pragma: no cover - optional dependency - from microsoft.opentelemetry.a365.constants import HOSTING_INSTALL_HINT - logging.getLogger(__name__).warning(HOSTING_INSTALL_HINT) + _HOSTING_AVAILABLE = True + except ImportError: # pragma: no cover - optional dependency + # Stub silently; the warning is emitted in __init__ when the user + # actually instantiates the cache. Authorization = TurnContext = None + _HOSTING_AVAILABLE = False logger = logging.getLogger(__name__) @@ -60,6 +66,8 @@ class _Entry: def __init__(self) -> None: """Initialize the token cache.""" + if not _HOSTING_AVAILABLE: + logger.warning(HOSTING_INSTALL_HINT) self._map: dict[str, AgenticTokenCache._Entry] = {} self._lock = Lock() From 3d6f48e18390277851c9f4513671e7056338c9f5 Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Tue, 5 May 2026 08:24:55 -0700 Subject: [PATCH 05/15] Address comments --- MIGRATION_A365.md | 18 ++++++++++++------ src/microsoft/opentelemetry/a365/core/utils.py | 14 ++++++++++++++ .../hosting/middleware/baggage_middleware.py | 10 ++-------- .../observability_hosting_manager.py | 10 ++-------- .../middleware/output_logging_middleware.py | 10 ++-------- .../token_cache_helpers/agent_token_cache.py | 10 ++-------- 6 files changed, 34 insertions(+), 38 deletions(-) diff --git a/MIGRATION_A365.md b/MIGRATION_A365.md index 526089bd..404234f2 100644 --- a/MIGRATION_A365.md +++ b/MIGRATION_A365.md @@ -45,12 +45,18 @@ pip install "microsoft-opentelemetry[hosting]" > The `microsoft-agents-activity` and `microsoft-agents-hosting-core` > packages are now **optional** dependencies pulled in by the `[hosting]` > extra. Without them, importing `microsoft.opentelemetry.a365.hosting` -> logs a warning with an install hint and continues — the optional -> classes/types are stubbed to ``None`` so the module import does not -> crash. Actually instantiating or calling the hosting middleware -> (`BaggageMiddleware`, `ObservabilityHostingManager`, -> `OutputLoggingMiddleware`, `AgenticTokenCache`, etc.) will then fail -> at runtime, so install the `[hosting]` extra before relying on it. +> succeeds silently — the optional classes/types are stubbed to ``None`` +> so the module import does not crash. The install-hint warning is +> emitted only when you actually instantiate the hosting middleware +> (`BaggageMiddleware()`, `OutputLoggingMiddleware()`, +> `AgenticTokenCache()`) or call `ObservabilityHostingManager.configure(...)`, +> after which the call will fail at runtime because the underlying +> classes are unavailable. Install the hosting extra before relying on +> this layer: +> +> ``` +> pip install "microsoft-opentelemetry[hosting]" +> ``` ## Step 2 — Rewrite Import Paths diff --git a/src/microsoft/opentelemetry/a365/core/utils.py b/src/microsoft/opentelemetry/a365/core/utils.py index d7018bdc..23b654f0 100644 --- a/src/microsoft/opentelemetry/a365/core/utils.py +++ b/src/microsoft/opentelemetry/a365/core/utils.py @@ -25,9 +25,23 @@ from wrapt import ObjectProxy from microsoft.opentelemetry.a365.core.constants import ERROR_TYPE_KEY +from microsoft.opentelemetry.a365.constants import HOSTING_INSTALL_HINT # mypy: disable-error-code="no-untyped-def" + +def warn_if_hosting_missing(logger: logging.Logger, *modules: str) -> None: + """Log :data:`HOSTING_INSTALL_HINT` on *logger* if any *modules* can't be imported.""" + import importlib # noqa: PLC0415 pylint: disable=import-outside-toplevel + + for name in modules: + try: + importlib.import_module(name) + except ImportError: + logger.warning(HOSTING_INSTALL_HINT) + return + + logger = logging.getLogger(__name__) logger.addHandler(logging.NullHandler()) diff --git a/src/microsoft/opentelemetry/a365/hosting/middleware/baggage_middleware.py b/src/microsoft/opentelemetry/a365/hosting/middleware/baggage_middleware.py index 28bb68d3..c191f713 100644 --- a/src/microsoft/opentelemetry/a365/hosting/middleware/baggage_middleware.py +++ b/src/microsoft/opentelemetry/a365/hosting/middleware/baggage_middleware.py @@ -7,7 +7,7 @@ import logging from collections.abc import Awaitable, Callable -from microsoft.opentelemetry.a365.constants import HOSTING_INSTALL_HINT +from microsoft.opentelemetry.a365.core.utils import warn_if_hosting_missing from microsoft.opentelemetry.a365.core.middleware.baggage_builder import BaggageBuilder from microsoft.opentelemetry.a365.hosting.scope_helpers.populate_baggage import populate @@ -17,19 +17,14 @@ if TYPE_CHECKING: from microsoft_agents.activity import ActivityEventNames, ActivityTypes from microsoft_agents.hosting.core.turn_context import TurnContext - - _HOSTING_AVAILABLE = True else: # pyright: ignore[reportUnreachable] try: from microsoft_agents.activity import ActivityEventNames, ActivityTypes from microsoft_agents.hosting.core.turn_context import TurnContext - - _HOSTING_AVAILABLE = True except ImportError: # pragma: no cover - optional dependency # Stub silently; the warning is emitted in __init__ when the user # actually instantiates the middleware. ActivityEventNames = ActivityTypes = TurnContext = None - _HOSTING_AVAILABLE = False # mypy: disable-error-code="call-arg" @@ -43,8 +38,7 @@ class BaggageMiddleware: """ def __init__(self) -> None: - if not _HOSTING_AVAILABLE: - _logger.warning(HOSTING_INSTALL_HINT) + warn_if_hosting_missing(_logger, "microsoft_agents.activity", "microsoft_agents.hosting.core") async def on_turn( self, diff --git a/src/microsoft/opentelemetry/a365/hosting/middleware/observability_hosting_manager.py b/src/microsoft/opentelemetry/a365/hosting/middleware/observability_hosting_manager.py index 0f8f9049..5455d6dc 100644 --- a/src/microsoft/opentelemetry/a365/hosting/middleware/observability_hosting_manager.py +++ b/src/microsoft/opentelemetry/a365/hosting/middleware/observability_hosting_manager.py @@ -10,24 +10,19 @@ from microsoft.opentelemetry.a365.hosting.middleware.baggage_middleware import BaggageMiddleware from microsoft.opentelemetry.a365.hosting.middleware.output_logging_middleware import OutputLoggingMiddleware -from microsoft.opentelemetry.a365.constants import HOSTING_INSTALL_HINT +from microsoft.opentelemetry.a365.core.utils import warn_if_hosting_missing from typing import TYPE_CHECKING if TYPE_CHECKING: from microsoft_agents.hosting.core.middleware_set import MiddlewareSet - - _HOSTING_AVAILABLE = True else: # pyright: ignore[reportUnreachable] try: from microsoft_agents.hosting.core.middleware_set import MiddlewareSet - - _HOSTING_AVAILABLE = True except ImportError: # pragma: no cover - optional dependency # Stub silently; the warning is emitted in configure() when the user # actually attempts to wire up hosting middleware. MiddlewareSet = None - _HOSTING_AVAILABLE = False logger = logging.getLogger(__name__) @@ -80,8 +75,7 @@ def configure( Raises: TypeError: If *middleware_set* or *options* is ``None``. """ - if not _HOSTING_AVAILABLE: - logger.warning(HOSTING_INSTALL_HINT) + warn_if_hosting_missing(logger, "microsoft_agents.hosting.core") if middleware_set is None: raise TypeError("middleware_set must not be None") if options is None: diff --git a/src/microsoft/opentelemetry/a365/hosting/middleware/output_logging_middleware.py b/src/microsoft/opentelemetry/a365/hosting/middleware/output_logging_middleware.py index 347b4112..505fec3d 100644 --- a/src/microsoft/opentelemetry/a365/hosting/middleware/output_logging_middleware.py +++ b/src/microsoft/opentelemetry/a365/hosting/middleware/output_logging_middleware.py @@ -19,26 +19,21 @@ from microsoft.opentelemetry.a365.core.spans_scopes.output_scope import OutputScope from microsoft.opentelemetry.a365.core.utils import extract_context_from_headers -from microsoft.opentelemetry.a365.constants import HOSTING_INSTALL_HINT +from microsoft.opentelemetry.a365.core.utils import warn_if_hosting_missing from typing import TYPE_CHECKING if TYPE_CHECKING: from microsoft_agents.activity import Activity from microsoft_agents.hosting.core.turn_context import TurnContext - - _HOSTING_AVAILABLE = True else: # pyright: ignore[reportUnreachable] try: from microsoft_agents.activity import Activity from microsoft_agents.hosting.core.turn_context import TurnContext - - _HOSTING_AVAILABLE = True except ImportError: # pragma: no cover - optional dependency # Stub silently; the warning is emitted in __init__ when the user # actually instantiates the middleware. Activity = TurnContext = None - _HOSTING_AVAILABLE = False # mypy: disable-error-code="call-arg" @@ -116,8 +111,7 @@ class OutputLoggingMiddleware: """ def __init__(self) -> None: - if not _HOSTING_AVAILABLE: - logger.warning(HOSTING_INSTALL_HINT) + warn_if_hosting_missing(logger, "microsoft_agents.activity", "microsoft_agents.hosting.core") async def on_turn( self, diff --git a/src/microsoft/opentelemetry/a365/hosting/token_cache_helpers/agent_token_cache.py b/src/microsoft/opentelemetry/a365/hosting/token_cache_helpers/agent_token_cache.py index d63f0fe6..bbd79ad2 100644 --- a/src/microsoft/opentelemetry/a365/hosting/token_cache_helpers/agent_token_cache.py +++ b/src/microsoft/opentelemetry/a365/hosting/token_cache_helpers/agent_token_cache.py @@ -12,24 +12,19 @@ from threading import Lock from typing import TYPE_CHECKING -from microsoft.opentelemetry.a365.constants import HOSTING_INSTALL_HINT +from microsoft.opentelemetry.a365.core.utils import warn_if_hosting_missing if TYPE_CHECKING: from microsoft_agents.hosting.core.app.oauth.authorization import Authorization from microsoft_agents.hosting.core.turn_context import TurnContext - - _HOSTING_AVAILABLE = True else: # pyright: ignore[reportUnreachable] try: from microsoft_agents.hosting.core.app.oauth.authorization import Authorization from microsoft_agents.hosting.core.turn_context import TurnContext - - _HOSTING_AVAILABLE = True except ImportError: # pragma: no cover - optional dependency # Stub silently; the warning is emitted in __init__ when the user # actually instantiates the cache. Authorization = TurnContext = None - _HOSTING_AVAILABLE = False logger = logging.getLogger(__name__) @@ -66,8 +61,7 @@ class _Entry: def __init__(self) -> None: """Initialize the token cache.""" - if not _HOSTING_AVAILABLE: - logger.warning(HOSTING_INSTALL_HINT) + warn_if_hosting_missing(logger, "microsoft_agents.hosting.core") self._map: dict[str, AgenticTokenCache._Entry] = {} self._lock = Lock() From 0e4cd790a4bb1e2be900b35a51e56101118cc0c8 Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Tue, 5 May 2026 08:46:06 -0700 Subject: [PATCH 06/15] Fix lint --- src/microsoft/opentelemetry/a365/core/utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/microsoft/opentelemetry/a365/core/utils.py b/src/microsoft/opentelemetry/a365/core/utils.py index 23b654f0..43018d70 100644 --- a/src/microsoft/opentelemetry/a365/core/utils.py +++ b/src/microsoft/opentelemetry/a365/core/utils.py @@ -30,15 +30,15 @@ # mypy: disable-error-code="no-untyped-def" -def warn_if_hosting_missing(logger: logging.Logger, *modules: str) -> None: - """Log :data:`HOSTING_INSTALL_HINT` on *logger* if any *modules* can't be imported.""" +def warn_if_hosting_missing(target_logger: logging.Logger, *modules: str) -> None: + """Log :data:`HOSTING_INSTALL_HINT` on *target_logger* if any *modules* can't be imported.""" import importlib # noqa: PLC0415 pylint: disable=import-outside-toplevel for name in modules: try: importlib.import_module(name) except ImportError: - logger.warning(HOSTING_INSTALL_HINT) + target_logger.warning(HOSTING_INSTALL_HINT) return From bcdc2b2428a2d1531dc5111aaf7977449e730411 Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Tue, 5 May 2026 14:42:49 -0700 Subject: [PATCH 07/15] Cleanup and simplify logic --- MIGRATION_A365.md | 24 ++++------------- pyproject.toml | 4 --- src/microsoft/opentelemetry/a365/constants.py | 7 ----- .../opentelemetry/a365/core/utils.py | 13 --------- .../hosting/middleware/baggage_middleware.py | 25 +++-------------- .../observability_hosting_manager.py | 18 +++---------- .../middleware/output_logging_middleware.py | 27 +++---------------- .../hosting/scope_helpers/populate_baggage.py | 17 +++--------- .../populate_invoke_agent_scope.py | 6 +---- .../a365/hosting/scope_helpers/utils.py | 16 +++-------- .../token_cache_helpers/agent_token_cache.py | 19 +++---------- .../test_observability_hosting_manager.py | 4 --- 12 files changed, 27 insertions(+), 153 deletions(-) diff --git a/MIGRATION_A365.md b/MIGRATION_A365.md index 404234f2..cb5dbc11 100644 --- a/MIGRATION_A365.md +++ b/MIGRATION_A365.md @@ -36,27 +36,13 @@ pip uninstall -y microsoft-agents-a365-observability-extensions-agent-framework # ✅ Install the new single package pip install microsoft-opentelemetry - -# If you use the hosting middleware (BaggageMiddleware, -# ObservabilityHostingManager, etc.), also install the hosting extra: -pip install "microsoft-opentelemetry[hosting]" ``` -> The `microsoft-agents-activity` and `microsoft-agents-hosting-core` -> packages are now **optional** dependencies pulled in by the `[hosting]` -> extra. Without them, importing `microsoft.opentelemetry.a365.hosting` -> succeeds silently — the optional classes/types are stubbed to ``None`` -> so the module import does not crash. The install-hint warning is -> emitted only when you actually instantiate the hosting middleware -> (`BaggageMiddleware()`, `OutputLoggingMiddleware()`, -> `AgenticTokenCache()`) or call `ObservabilityHostingManager.configure(...)`, -> after which the call will fail at runtime because the underlying -> classes are unavailable. Install the hosting extra before relying on -> this layer: -> -> ``` -> pip install "microsoft-opentelemetry[hosting]" -> ``` +> If you use the hosting middleware (`BaggageMiddleware`, +> `ObservabilityHostingManager`, etc.), also install +> `microsoft-agents-activity` and `microsoft-agents-hosting-core`. If +> they are not installed, importing from +> `microsoft.opentelemetry.a365.hosting` raises an `ImportError`. ## Step 2 — Rewrite Import Paths diff --git a/pyproject.toml b/pyproject.toml index 30e6970b..1da9331e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,10 +47,6 @@ dependencies = [ ] [project.optional-dependencies] -hosting = [ - "microsoft-agents-activity>=0.9.0", - "microsoft-agents-hosting-core>=0.9.0", -] langchain = [ "langchain-core>=0.2.0", ] diff --git a/src/microsoft/opentelemetry/a365/constants.py b/src/microsoft/opentelemetry/a365/constants.py index dfe57097..f02c03f5 100644 --- a/src/microsoft/opentelemetry/a365/constants.py +++ b/src/microsoft/opentelemetry/a365/constants.py @@ -132,10 +132,3 @@ A365_SERVICE_CLIENT_ID_ENV = "CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTID" A365_SERVICE_CLIENT_SECRET_ENV = "CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTSECRET" A365_SERVICE_TENANT_ID_ENV = "CONNECTIONS__SERVICE_CONNECTION__SETTINGS__TENANTID" - -# --- Optional dependency install hints --- -HOSTING_INSTALL_HINT = ( - "microsoft.opentelemetry.a365.hosting requires 'microsoft-agents-activity>=0.9.0' " - "and 'microsoft-agents-hosting-core>=0.9.0'. Install them with: " - 'pip install "microsoft-opentelemetry[hosting]"' -) diff --git a/src/microsoft/opentelemetry/a365/core/utils.py b/src/microsoft/opentelemetry/a365/core/utils.py index 43018d70..c0b5cd0e 100644 --- a/src/microsoft/opentelemetry/a365/core/utils.py +++ b/src/microsoft/opentelemetry/a365/core/utils.py @@ -25,23 +25,10 @@ from wrapt import ObjectProxy from microsoft.opentelemetry.a365.core.constants import ERROR_TYPE_KEY -from microsoft.opentelemetry.a365.constants import HOSTING_INSTALL_HINT # mypy: disable-error-code="no-untyped-def" -def warn_if_hosting_missing(target_logger: logging.Logger, *modules: str) -> None: - """Log :data:`HOSTING_INSTALL_HINT` on *target_logger* if any *modules* can't be imported.""" - import importlib # noqa: PLC0415 pylint: disable=import-outside-toplevel - - for name in modules: - try: - importlib.import_module(name) - except ImportError: - target_logger.warning(HOSTING_INSTALL_HINT) - return - - logger = logging.getLogger(__name__) logger.addHandler(logging.NullHandler()) diff --git a/src/microsoft/opentelemetry/a365/hosting/middleware/baggage_middleware.py b/src/microsoft/opentelemetry/a365/hosting/middleware/baggage_middleware.py index c191f713..57c63663 100644 --- a/src/microsoft/opentelemetry/a365/hosting/middleware/baggage_middleware.py +++ b/src/microsoft/opentelemetry/a365/hosting/middleware/baggage_middleware.py @@ -7,26 +7,12 @@ import logging from collections.abc import Awaitable, Callable -from microsoft.opentelemetry.a365.core.utils import warn_if_hosting_missing -from microsoft.opentelemetry.a365.core.middleware.baggage_builder import BaggageBuilder - -from microsoft.opentelemetry.a365.hosting.scope_helpers.populate_baggage import populate - -from typing import TYPE_CHECKING -if TYPE_CHECKING: - from microsoft_agents.activity import ActivityEventNames, ActivityTypes - from microsoft_agents.hosting.core.turn_context import TurnContext -else: # pyright: ignore[reportUnreachable] - try: - from microsoft_agents.activity import ActivityEventNames, ActivityTypes - from microsoft_agents.hosting.core.turn_context import TurnContext - except ImportError: # pragma: no cover - optional dependency - # Stub silently; the warning is emitted in __init__ when the user - # actually instantiates the middleware. - ActivityEventNames = ActivityTypes = TurnContext = None +from microsoft_agents.activity import ActivityEventNames, ActivityTypes +from microsoft_agents.hosting.core.turn_context import TurnContext -# mypy: disable-error-code="call-arg" +from microsoft.opentelemetry.a365.core.middleware.baggage_builder import BaggageBuilder +from microsoft.opentelemetry.a365.hosting.scope_helpers.populate_baggage import populate _logger = logging.getLogger(__name__) @@ -37,9 +23,6 @@ class BaggageMiddleware: Async replies (ContinueConversation) are passed through without baggage setup. """ - def __init__(self) -> None: - warn_if_hosting_missing(_logger, "microsoft_agents.activity", "microsoft_agents.hosting.core") - async def on_turn( self, context: TurnContext, diff --git a/src/microsoft/opentelemetry/a365/hosting/middleware/observability_hosting_manager.py b/src/microsoft/opentelemetry/a365/hosting/middleware/observability_hosting_manager.py index 5455d6dc..0c8f9885 100644 --- a/src/microsoft/opentelemetry/a365/hosting/middleware/observability_hosting_manager.py +++ b/src/microsoft/opentelemetry/a365/hosting/middleware/observability_hosting_manager.py @@ -7,22 +7,11 @@ import logging from dataclasses import dataclass -from microsoft.opentelemetry.a365.hosting.middleware.baggage_middleware import BaggageMiddleware -from microsoft.opentelemetry.a365.hosting.middleware.output_logging_middleware import OutputLoggingMiddleware - -from microsoft.opentelemetry.a365.core.utils import warn_if_hosting_missing -from typing import TYPE_CHECKING +from microsoft_agents.hosting.core.middleware_set import MiddlewareSet -if TYPE_CHECKING: - from microsoft_agents.hosting.core.middleware_set import MiddlewareSet -else: # pyright: ignore[reportUnreachable] - try: - from microsoft_agents.hosting.core.middleware_set import MiddlewareSet - except ImportError: # pragma: no cover - optional dependency - # Stub silently; the warning is emitted in configure() when the user - # actually attempts to wire up hosting middleware. - MiddlewareSet = None +from microsoft.opentelemetry.a365.hosting.middleware.baggage_middleware import BaggageMiddleware +from microsoft.opentelemetry.a365.hosting.middleware.output_logging_middleware import OutputLoggingMiddleware logger = logging.getLogger(__name__) @@ -75,7 +64,6 @@ def configure( Raises: TypeError: If *middleware_set* or *options* is ``None``. """ - warn_if_hosting_missing(logger, "microsoft_agents.hosting.core") if middleware_set is None: raise TypeError("middleware_set must not be None") if options is None: diff --git a/src/microsoft/opentelemetry/a365/hosting/middleware/output_logging_middleware.py b/src/microsoft/opentelemetry/a365/hosting/middleware/output_logging_middleware.py index 505fec3d..fcaf1946 100644 --- a/src/microsoft/opentelemetry/a365/hosting/middleware/output_logging_middleware.py +++ b/src/microsoft/opentelemetry/a365/hosting/middleware/output_logging_middleware.py @@ -8,6 +8,9 @@ import logging from collections.abc import Awaitable, Callable from microsoft.opentelemetry.a365.core.agent_details import AgentDetails +from microsoft_agents.activity import Activity, ActivityTypes +from microsoft_agents.hosting.core.turn_context import TurnContext + from microsoft.opentelemetry.a365.constants import ( CHANNEL_LINK_KEY, CHANNEL_NAME_KEY, @@ -19,25 +22,6 @@ from microsoft.opentelemetry.a365.core.spans_scopes.output_scope import OutputScope from microsoft.opentelemetry.a365.core.utils import extract_context_from_headers -from microsoft.opentelemetry.a365.core.utils import warn_if_hosting_missing - -from typing import TYPE_CHECKING - -if TYPE_CHECKING: - from microsoft_agents.activity import Activity - from microsoft_agents.hosting.core.turn_context import TurnContext -else: # pyright: ignore[reportUnreachable] - try: - from microsoft_agents.activity import Activity - from microsoft_agents.hosting.core.turn_context import TurnContext - except ImportError: # pragma: no cover - optional dependency - # Stub silently; the warning is emitted in __init__ when the user - # actually instantiates the middleware. - Activity = TurnContext = None - - -# mypy: disable-error-code="call-arg" - logger = logging.getLogger(__name__) # TurnState key for the parent trace context (W3C traceparent string). @@ -110,9 +94,6 @@ class OutputLoggingMiddleware: attributes and exported to the configured telemetry backend. """ - def __init__(self) -> None: - warn_if_hosting_missing(logger, "microsoft_agents.activity", "microsoft_agents.hosting.core") - async def on_turn( self, context: TurnContext, @@ -158,7 +139,7 @@ async def handler( activities: list[Activity], send_next: Callable, ) -> None: - messages = [a.text for a in activities if getattr(a, "type", None) == "message" and a.text] + messages = [a.text for a in activities if getattr(a, "type", None) == ActivityTypes.message and a.text] if not messages: await send_next() diff --git a/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_baggage.py b/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_baggage.py index f2e7908e..11639677 100644 --- a/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_baggage.py +++ b/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_baggage.py @@ -4,9 +4,11 @@ from __future__ import annotations from collections.abc import Iterator +from typing import Any -from microsoft.opentelemetry.a365.core.middleware.baggage_builder import BaggageBuilder +from microsoft_agents.hosting.core.turn_context import TurnContext +from microsoft.opentelemetry.a365.core.middleware.baggage_builder import BaggageBuilder from microsoft.opentelemetry.a365.hosting.scope_helpers.utils import ( get_caller_pairs, get_channel_pairs, @@ -14,19 +16,6 @@ get_target_agent_pairs, get_tenant_id_pair, ) -from typing import TYPE_CHECKING, Any - -if TYPE_CHECKING: - from microsoft_agents.hosting.core.turn_context import TurnContext -else: # pyright: ignore[reportUnreachable] - try: - from microsoft_agents.hosting.core.turn_context import TurnContext - except ImportError: # pragma: no cover - optional dependency - # Stub silently; warning is emitted by the user-facing entry-point - # (middleware/cache constructors) via HOSTING_INSTALL_HINT. - TurnContext = None - - def _iter_all_pairs(turn_context: TurnContext) -> Iterator[tuple[str, Any]]: diff --git a/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_invoke_agent_scope.py b/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_invoke_agent_scope.py index 971bc794..81455352 100644 --- a/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_invoke_agent_scope.py +++ b/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_invoke_agent_scope.py @@ -3,10 +3,9 @@ from __future__ import annotations -from typing import TYPE_CHECKING +from microsoft_agents.hosting.core.turn_context import TurnContext from microsoft.opentelemetry.a365.core.invoke_agent_scope import InvokeAgentScope - from microsoft.opentelemetry.a365.hosting.scope_helpers.utils import ( get_caller_pairs, get_channel_pairs, @@ -15,9 +14,6 @@ get_tenant_id_pair, ) -if TYPE_CHECKING: - from microsoft_agents.hosting.core.turn_context import TurnContext - # mypy: disable-error-code="arg-type" diff --git a/src/microsoft/opentelemetry/a365/hosting/scope_helpers/utils.py b/src/microsoft/opentelemetry/a365/hosting/scope_helpers/utils.py index 1f23468f..7ee7e2ff 100644 --- a/src/microsoft/opentelemetry/a365/hosting/scope_helpers/utils.py +++ b/src/microsoft/opentelemetry/a365/hosting/scope_helpers/utils.py @@ -4,6 +4,10 @@ from __future__ import annotations from collections.abc import Iterator +from typing import Any + +from microsoft_agents.activity import Activity + from microsoft.opentelemetry.a365.constants import ( CHANNEL_LINK_KEY, CHANNEL_NAME_KEY, @@ -19,18 +23,6 @@ USER_ID_KEY, USER_NAME_KEY, ) -from typing import TYPE_CHECKING, Any - -if TYPE_CHECKING: - from microsoft_agents.activity import Activity -else: # pyright: ignore[reportUnreachable] - try: - from microsoft_agents.activity import Activity - except ImportError: # pragma: no cover - optional dependency - # Stub silently; warning is emitted by the user-facing entry-point - # (middleware/cache constructors) via HOSTING_INSTALL_HINT. - Activity = None - AGENT_ROLE = "agenticUser" diff --git a/src/microsoft/opentelemetry/a365/hosting/token_cache_helpers/agent_token_cache.py b/src/microsoft/opentelemetry/a365/hosting/token_cache_helpers/agent_token_cache.py index bbd79ad2..13ad2bae 100644 --- a/src/microsoft/opentelemetry/a365/hosting/token_cache_helpers/agent_token_cache.py +++ b/src/microsoft/opentelemetry/a365/hosting/token_cache_helpers/agent_token_cache.py @@ -10,21 +10,9 @@ import logging from dataclasses import dataclass from threading import Lock -from typing import TYPE_CHECKING - -from microsoft.opentelemetry.a365.core.utils import warn_if_hosting_missing - -if TYPE_CHECKING: - from microsoft_agents.hosting.core.app.oauth.authorization import Authorization - from microsoft_agents.hosting.core.turn_context import TurnContext -else: # pyright: ignore[reportUnreachable] - try: - from microsoft_agents.hosting.core.app.oauth.authorization import Authorization - from microsoft_agents.hosting.core.turn_context import TurnContext - except ImportError: # pragma: no cover - optional dependency - # Stub silently; the warning is emitted in __init__ when the user - # actually instantiates the cache. - Authorization = TurnContext = None + +from microsoft_agents.hosting.core.app.oauth.authorization import Authorization +from microsoft_agents.hosting.core.turn_context import TurnContext logger = logging.getLogger(__name__) @@ -61,7 +49,6 @@ class _Entry: def __init__(self) -> None: """Initialize the token cache.""" - warn_if_hosting_missing(logger, "microsoft_agents.hosting.core") self._map: dict[str, AgenticTokenCache._Entry] = {} self._lock = Lock() diff --git a/tests/a365/hosting/middleware/test_observability_hosting_manager.py b/tests/a365/hosting/middleware/test_observability_hosting_manager.py index dac0e2ae..e1361677 100644 --- a/tests/a365/hosting/middleware/test_observability_hosting_manager.py +++ b/tests/a365/hosting/middleware/test_observability_hosting_manager.py @@ -5,10 +5,6 @@ import pytest -pytest.importorskip("microsoft_agents.activity") -pytest.importorskip("microsoft_agents.hosting.core") - -# pylint: disable=wrong-import-position from microsoft.opentelemetry.a365.hosting.middleware.baggage_middleware import ( BaggageMiddleware, ) From 3abe107fd1bdd283d2bc3b52534b3737cd4da293 Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Tue, 5 May 2026 14:49:27 -0700 Subject: [PATCH 08/15] Bring back to original state --- .../a365/hosting/middleware/baggage_middleware.py | 6 ++---- .../a365/hosting/middleware/output_logging_middleware.py | 4 ++-- .../a365/hosting/scope_helpers/populate_baggage.py | 2 +- .../opentelemetry/a365/hosting/scope_helpers/utils.py | 4 ---- 4 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/microsoft/opentelemetry/a365/hosting/middleware/baggage_middleware.py b/src/microsoft/opentelemetry/a365/hosting/middleware/baggage_middleware.py index 57c63663..7b921999 100644 --- a/src/microsoft/opentelemetry/a365/hosting/middleware/baggage_middleware.py +++ b/src/microsoft/opentelemetry/a365/hosting/middleware/baggage_middleware.py @@ -5,16 +5,14 @@ from __future__ import annotations -import logging from collections.abc import Awaitable, Callable from microsoft_agents.activity import ActivityEventNames, ActivityTypes from microsoft_agents.hosting.core.turn_context import TurnContext - from microsoft.opentelemetry.a365.core.middleware.baggage_builder import BaggageBuilder -from microsoft.opentelemetry.a365.hosting.scope_helpers.populate_baggage import populate -_logger = logging.getLogger(__name__) + +from microsoft.opentelemetry.a365.hosting.scope_helpers.populate_baggage import populate class BaggageMiddleware: diff --git a/src/microsoft/opentelemetry/a365/hosting/middleware/output_logging_middleware.py b/src/microsoft/opentelemetry/a365/hosting/middleware/output_logging_middleware.py index fcaf1946..7b0b09b4 100644 --- a/src/microsoft/opentelemetry/a365/hosting/middleware/output_logging_middleware.py +++ b/src/microsoft/opentelemetry/a365/hosting/middleware/output_logging_middleware.py @@ -7,9 +7,9 @@ import logging from collections.abc import Awaitable, Callable -from microsoft.opentelemetry.a365.core.agent_details import AgentDetails from microsoft_agents.activity import Activity, ActivityTypes from microsoft_agents.hosting.core.turn_context import TurnContext +from microsoft.opentelemetry.a365.core.agent_details import AgentDetails from microsoft.opentelemetry.a365.constants import ( CHANNEL_LINK_KEY, @@ -139,7 +139,7 @@ async def handler( activities: list[Activity], send_next: Callable, ) -> None: - messages = [a.text for a in activities if getattr(a, "type", None) == ActivityTypes.message and a.text] + messages = [a.text for a in activities if getattr(a, "type", None) == "message" and a.text] if not messages: await send_next() diff --git a/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_baggage.py b/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_baggage.py index 11639677..78adeb27 100644 --- a/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_baggage.py +++ b/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_baggage.py @@ -6,9 +6,9 @@ from collections.abc import Iterator from typing import Any +from microsoft.opentelemetry.a365.core.middleware.baggage_builder import BaggageBuilder from microsoft_agents.hosting.core.turn_context import TurnContext -from microsoft.opentelemetry.a365.core.middleware.baggage_builder import BaggageBuilder from microsoft.opentelemetry.a365.hosting.scope_helpers.utils import ( get_caller_pairs, get_channel_pairs, diff --git a/src/microsoft/opentelemetry/a365/hosting/scope_helpers/utils.py b/src/microsoft/opentelemetry/a365/hosting/scope_helpers/utils.py index 7ee7e2ff..a02da39d 100644 --- a/src/microsoft/opentelemetry/a365/hosting/scope_helpers/utils.py +++ b/src/microsoft/opentelemetry/a365/hosting/scope_helpers/utils.py @@ -1,13 +1,10 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. -from __future__ import annotations - from collections.abc import Iterator from typing import Any from microsoft_agents.activity import Activity - from microsoft.opentelemetry.a365.constants import ( CHANNEL_LINK_KEY, CHANNEL_NAME_KEY, @@ -24,7 +21,6 @@ USER_NAME_KEY, ) - AGENT_ROLE = "agenticUser" From 5ccf313d2be3dbca3bb63656110786ce3295a48a Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Tue, 5 May 2026 15:52:05 -0700 Subject: [PATCH 09/15] Fix imports --- src/microsoft/opentelemetry/a365/core/utils.py | 1 - .../a365/hosting/middleware/baggage_middleware.py | 3 +-- .../a365/hosting/scope_helpers/populate_baggage.py | 2 +- .../a365/hosting/scope_helpers/populate_invoke_agent_scope.py | 1 - 4 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/microsoft/opentelemetry/a365/core/utils.py b/src/microsoft/opentelemetry/a365/core/utils.py index c0b5cd0e..d7018bdc 100644 --- a/src/microsoft/opentelemetry/a365/core/utils.py +++ b/src/microsoft/opentelemetry/a365/core/utils.py @@ -28,7 +28,6 @@ # mypy: disable-error-code="no-untyped-def" - logger = logging.getLogger(__name__) logger.addHandler(logging.NullHandler()) diff --git a/src/microsoft/opentelemetry/a365/hosting/middleware/baggage_middleware.py b/src/microsoft/opentelemetry/a365/hosting/middleware/baggage_middleware.py index 7b921999..38a00e07 100644 --- a/src/microsoft/opentelemetry/a365/hosting/middleware/baggage_middleware.py +++ b/src/microsoft/opentelemetry/a365/hosting/middleware/baggage_middleware.py @@ -8,13 +8,12 @@ from collections.abc import Awaitable, Callable from microsoft_agents.activity import ActivityEventNames, ActivityTypes + from microsoft_agents.hosting.core.turn_context import TurnContext from microsoft.opentelemetry.a365.core.middleware.baggage_builder import BaggageBuilder - from microsoft.opentelemetry.a365.hosting.scope_helpers.populate_baggage import populate - class BaggageMiddleware: """Middleware that propagates OpenTelemetry baggage context derived from TurnContext. diff --git a/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_baggage.py b/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_baggage.py index 78adeb27..f0e95bf1 100644 --- a/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_baggage.py +++ b/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_baggage.py @@ -6,8 +6,8 @@ from collections.abc import Iterator from typing import Any -from microsoft.opentelemetry.a365.core.middleware.baggage_builder import BaggageBuilder from microsoft_agents.hosting.core.turn_context import TurnContext +from microsoft.opentelemetry.a365.core.middleware.baggage_builder import BaggageBuilder from microsoft.opentelemetry.a365.hosting.scope_helpers.utils import ( get_caller_pairs, diff --git a/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_invoke_agent_scope.py b/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_invoke_agent_scope.py index 81455352..d1109ca0 100644 --- a/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_invoke_agent_scope.py +++ b/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_invoke_agent_scope.py @@ -4,7 +4,6 @@ from __future__ import annotations from microsoft_agents.hosting.core.turn_context import TurnContext - from microsoft.opentelemetry.a365.core.invoke_agent_scope import InvokeAgentScope from microsoft.opentelemetry.a365.hosting.scope_helpers.utils import ( get_caller_pairs, From 266bf1709ceccb2835b39745ac216dfe42950c74 Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Tue, 5 May 2026 15:59:18 -0700 Subject: [PATCH 10/15] Fix imports --- .../opentelemetry/a365/hosting/middleware/baggage_middleware.py | 1 - .../a365/hosting/middleware/output_logging_middleware.py | 2 +- .../a365/hosting/scope_helpers/populate_invoke_agent_scope.py | 1 + .../hosting/middleware/test_observability_hosting_manager.py | 1 - 4 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/microsoft/opentelemetry/a365/hosting/middleware/baggage_middleware.py b/src/microsoft/opentelemetry/a365/hosting/middleware/baggage_middleware.py index 38a00e07..4c17e355 100644 --- a/src/microsoft/opentelemetry/a365/hosting/middleware/baggage_middleware.py +++ b/src/microsoft/opentelemetry/a365/hosting/middleware/baggage_middleware.py @@ -8,7 +8,6 @@ from collections.abc import Awaitable, Callable from microsoft_agents.activity import ActivityEventNames, ActivityTypes - from microsoft_agents.hosting.core.turn_context import TurnContext from microsoft.opentelemetry.a365.core.middleware.baggage_builder import BaggageBuilder diff --git a/src/microsoft/opentelemetry/a365/hosting/middleware/output_logging_middleware.py b/src/microsoft/opentelemetry/a365/hosting/middleware/output_logging_middleware.py index 7b0b09b4..9786ff2a 100644 --- a/src/microsoft/opentelemetry/a365/hosting/middleware/output_logging_middleware.py +++ b/src/microsoft/opentelemetry/a365/hosting/middleware/output_logging_middleware.py @@ -7,10 +7,10 @@ import logging from collections.abc import Awaitable, Callable + from microsoft_agents.activity import Activity, ActivityTypes from microsoft_agents.hosting.core.turn_context import TurnContext from microsoft.opentelemetry.a365.core.agent_details import AgentDetails - from microsoft.opentelemetry.a365.constants import ( CHANNEL_LINK_KEY, CHANNEL_NAME_KEY, diff --git a/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_invoke_agent_scope.py b/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_invoke_agent_scope.py index d1109ca0..8cdd0609 100644 --- a/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_invoke_agent_scope.py +++ b/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_invoke_agent_scope.py @@ -5,6 +5,7 @@ from microsoft_agents.hosting.core.turn_context import TurnContext from microsoft.opentelemetry.a365.core.invoke_agent_scope import InvokeAgentScope + from microsoft.opentelemetry.a365.hosting.scope_helpers.utils import ( get_caller_pairs, get_channel_pairs, diff --git a/tests/a365/hosting/middleware/test_observability_hosting_manager.py b/tests/a365/hosting/middleware/test_observability_hosting_manager.py index e1361677..1c3c200e 100644 --- a/tests/a365/hosting/middleware/test_observability_hosting_manager.py +++ b/tests/a365/hosting/middleware/test_observability_hosting_manager.py @@ -4,7 +4,6 @@ from unittest.mock import MagicMock import pytest - from microsoft.opentelemetry.a365.hosting.middleware.baggage_middleware import ( BaggageMiddleware, ) From 17379d3d56eba599d8fc51b4b4ad3e2f4fbf2b3e Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Tue, 5 May 2026 16:03:57 -0700 Subject: [PATCH 11/15] Fix indentation --- .../a365/hosting/middleware/baggage_middleware.py | 3 +++ .../a365/hosting/middleware/output_logging_middleware.py | 4 +++- .../hosting/scope_helpers/populate_invoke_agent_scope.py | 5 ++++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/microsoft/opentelemetry/a365/hosting/middleware/baggage_middleware.py b/src/microsoft/opentelemetry/a365/hosting/middleware/baggage_middleware.py index 4c17e355..d2f469e8 100644 --- a/src/microsoft/opentelemetry/a365/hosting/middleware/baggage_middleware.py +++ b/src/microsoft/opentelemetry/a365/hosting/middleware/baggage_middleware.py @@ -13,6 +13,9 @@ from microsoft.opentelemetry.a365.hosting.scope_helpers.populate_baggage import populate +# mypy: disable-error-code="call-arg" + + class BaggageMiddleware: """Middleware that propagates OpenTelemetry baggage context derived from TurnContext. diff --git a/src/microsoft/opentelemetry/a365/hosting/middleware/output_logging_middleware.py b/src/microsoft/opentelemetry/a365/hosting/middleware/output_logging_middleware.py index 9786ff2a..34411d9a 100644 --- a/src/microsoft/opentelemetry/a365/hosting/middleware/output_logging_middleware.py +++ b/src/microsoft/opentelemetry/a365/hosting/middleware/output_logging_middleware.py @@ -8,7 +8,7 @@ import logging from collections.abc import Awaitable, Callable -from microsoft_agents.activity import Activity, ActivityTypes +from microsoft_agents.activity import Activity from microsoft_agents.hosting.core.turn_context import TurnContext from microsoft.opentelemetry.a365.core.agent_details import AgentDetails from microsoft.opentelemetry.a365.constants import ( @@ -22,6 +22,8 @@ from microsoft.opentelemetry.a365.core.spans_scopes.output_scope import OutputScope from microsoft.opentelemetry.a365.core.utils import extract_context_from_headers +# mypy: disable-error-code="call-arg" + logger = logging.getLogger(__name__) # TurnState key for the parent trace context (W3C traceparent string). diff --git a/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_invoke_agent_scope.py b/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_invoke_agent_scope.py index 8cdd0609..ec289472 100644 --- a/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_invoke_agent_scope.py +++ b/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_invoke_agent_scope.py @@ -3,7 +3,8 @@ from __future__ import annotations -from microsoft_agents.hosting.core.turn_context import TurnContext +from typing import TYPE_CHECKING + from microsoft.opentelemetry.a365.core.invoke_agent_scope import InvokeAgentScope from microsoft.opentelemetry.a365.hosting.scope_helpers.utils import ( @@ -16,6 +17,8 @@ # mypy: disable-error-code="arg-type" +if TYPE_CHECKING: + from microsoft_agents.hosting.core.turn_context import TurnContext def populate(scope: InvokeAgentScope, turn_context: TurnContext) -> InvokeAgentScope: """ From 274a9eaae748a0bd5e227f169e54d4ce47943361 Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Tue, 5 May 2026 16:15:31 -0700 Subject: [PATCH 12/15] Fix file --- .../a365/hosting/scope_helpers/populate_invoke_agent_scope.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_invoke_agent_scope.py b/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_invoke_agent_scope.py index ec289472..8b6d9ce3 100644 --- a/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_invoke_agent_scope.py +++ b/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_invoke_agent_scope.py @@ -15,11 +15,11 @@ get_tenant_id_pair, ) -# mypy: disable-error-code="arg-type" - if TYPE_CHECKING: from microsoft_agents.hosting.core.turn_context import TurnContext +# mypy: disable-error-code="arg-type" + def populate(scope: InvokeAgentScope, turn_context: TurnContext) -> InvokeAgentScope: """ Populate all supported InvokeAgentScope tags from the provided TurnContext. From bb6b8dd65d9304ebd17a5f56e7e6cf74889d7b63 Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Tue, 5 May 2026 16:16:36 -0700 Subject: [PATCH 13/15] Add extra line --- .../a365/hosting/scope_helpers/populate_invoke_agent_scope.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_invoke_agent_scope.py b/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_invoke_agent_scope.py index 8b6d9ce3..971bc794 100644 --- a/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_invoke_agent_scope.py +++ b/src/microsoft/opentelemetry/a365/hosting/scope_helpers/populate_invoke_agent_scope.py @@ -20,6 +20,7 @@ # mypy: disable-error-code="arg-type" + def populate(scope: InvokeAgentScope, turn_context: TurnContext) -> InvokeAgentScope: """ Populate all supported InvokeAgentScope tags from the provided TurnContext. From 4a6a7bce070e3518886db43715ec7524247f8806 Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Tue, 5 May 2026 16:28:02 -0700 Subject: [PATCH 14/15] Clean up tests --- dev_requirements.txt | 2 ++ tests/a365/hosting/middleware/test_baggage_middleware.py | 4 ---- .../hosting/middleware/test_output_logging_middleware.py | 4 ---- tests/a365/hosting/scope_helpers/test_populate_baggage.py | 6 ------ .../scope_helpers/test_populate_invoke_agent_scope.py | 4 ---- tests/a365/hosting/scope_helpers/test_scope_helper_utils.py | 5 ----- .../hosting/token_cache_helpers/test_agent_token_cache.py | 3 --- 7 files changed, 2 insertions(+), 26 deletions(-) diff --git a/dev_requirements.txt b/dev_requirements.txt index 37e0ebe1..09a15ff8 100644 --- a/dev_requirements.txt +++ b/dev_requirements.txt @@ -2,3 +2,5 @@ pytest>=8.0 pytest-asyncio>=0.23.0 pytest-cov>=5.0 black>=24.0 +microsoft-agents-activity +microsoft-agents-hosting-core diff --git a/tests/a365/hosting/middleware/test_baggage_middleware.py b/tests/a365/hosting/middleware/test_baggage_middleware.py index d4e3a6b0..00f87c9e 100644 --- a/tests/a365/hosting/middleware/test_baggage_middleware.py +++ b/tests/a365/hosting/middleware/test_baggage_middleware.py @@ -5,10 +5,6 @@ import pytest -pytest.importorskip("microsoft_agents.activity") -pytest.importorskip("microsoft_agents.hosting.core") - -# pylint: disable=wrong-import-position from microsoft_agents.activity import ( Activity, ActivityEventNames, diff --git a/tests/a365/hosting/middleware/test_output_logging_middleware.py b/tests/a365/hosting/middleware/test_output_logging_middleware.py index fbeabf8d..100ced37 100644 --- a/tests/a365/hosting/middleware/test_output_logging_middleware.py +++ b/tests/a365/hosting/middleware/test_output_logging_middleware.py @@ -5,10 +5,6 @@ import pytest -pytest.importorskip("microsoft_agents.activity") -pytest.importorskip("microsoft_agents.hosting.core") - -# pylint: disable=wrong-import-position from microsoft_agents.activity import ( Activity, ChannelAccount, diff --git a/tests/a365/hosting/scope_helpers/test_populate_baggage.py b/tests/a365/hosting/scope_helpers/test_populate_baggage.py index e8a1af51..46222ddb 100644 --- a/tests/a365/hosting/scope_helpers/test_populate_baggage.py +++ b/tests/a365/hosting/scope_helpers/test_populate_baggage.py @@ -3,12 +3,6 @@ from unittest.mock import MagicMock -import pytest - -pytest.importorskip("microsoft_agents.activity") -pytest.importorskip("microsoft_agents.hosting.core") - -# pylint: disable=wrong-import-position from microsoft_agents.activity import Activity, ChannelAccount, ConversationAccount from microsoft_agents.hosting.core import TurnContext from microsoft.opentelemetry.a365.core.constants import USER_ID_KEY diff --git a/tests/a365/hosting/scope_helpers/test_populate_invoke_agent_scope.py b/tests/a365/hosting/scope_helpers/test_populate_invoke_agent_scope.py index 2a70f852..b5f69273 100644 --- a/tests/a365/hosting/scope_helpers/test_populate_invoke_agent_scope.py +++ b/tests/a365/hosting/scope_helpers/test_populate_invoke_agent_scope.py @@ -6,10 +6,6 @@ import pytest -pytest.importorskip("microsoft_agents.activity") -pytest.importorskip("microsoft_agents.hosting.core") - -# pylint: disable=wrong-import-position from microsoft_agents.activity import Activity, ChannelAccount, ConversationAccount from microsoft_agents.hosting.core import TurnContext from microsoft.opentelemetry.a365.core.agent_details import AgentDetails diff --git a/tests/a365/hosting/scope_helpers/test_scope_helper_utils.py b/tests/a365/hosting/scope_helpers/test_scope_helper_utils.py index d4582aee..8d0ee845 100644 --- a/tests/a365/hosting/scope_helpers/test_scope_helper_utils.py +++ b/tests/a365/hosting/scope_helpers/test_scope_helper_utils.py @@ -1,11 +1,6 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. -import pytest - -pytest.importorskip("microsoft_agents.activity") - -# pylint: disable=wrong-import-position from microsoft_agents.activity import Activity, ChannelAccount, ConversationAccount from microsoft.opentelemetry.a365.core.constants import ( CHANNEL_LINK_KEY, diff --git a/tests/a365/hosting/token_cache_helpers/test_agent_token_cache.py b/tests/a365/hosting/token_cache_helpers/test_agent_token_cache.py index 28244a15..7d5748e2 100644 --- a/tests/a365/hosting/token_cache_helpers/test_agent_token_cache.py +++ b/tests/a365/hosting/token_cache_helpers/test_agent_token_cache.py @@ -8,9 +8,6 @@ import pytest -pytest.importorskip("microsoft_agents.hosting.core") - -# pylint: disable=wrong-import-position from microsoft_agents.hosting.core.app.oauth.authorization import Authorization from microsoft_agents.hosting.core.turn_context import TurnContext from microsoft.opentelemetry.a365.hosting.token_cache_helpers import ( From 69b17937e0a8cf9cc90c081ca412476ea2a623d4 Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Tue, 5 May 2026 16:31:56 -0700 Subject: [PATCH 15/15] Fix lint --- tests/a365/hosting/middleware/test_baggage_middleware.py | 1 - tests/a365/hosting/middleware/test_output_logging_middleware.py | 1 - tests/a365/hosting/scope_helpers/test_populate_baggage.py | 1 + .../hosting/scope_helpers/test_populate_invoke_agent_scope.py | 1 - tests/a365/hosting/token_cache_helpers/test_agent_token_cache.py | 1 - 5 files changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/a365/hosting/middleware/test_baggage_middleware.py b/tests/a365/hosting/middleware/test_baggage_middleware.py index 00f87c9e..04d38864 100644 --- a/tests/a365/hosting/middleware/test_baggage_middleware.py +++ b/tests/a365/hosting/middleware/test_baggage_middleware.py @@ -4,7 +4,6 @@ from unittest.mock import MagicMock import pytest - from microsoft_agents.activity import ( Activity, ActivityEventNames, diff --git a/tests/a365/hosting/middleware/test_output_logging_middleware.py b/tests/a365/hosting/middleware/test_output_logging_middleware.py index 100ced37..58805a94 100644 --- a/tests/a365/hosting/middleware/test_output_logging_middleware.py +++ b/tests/a365/hosting/middleware/test_output_logging_middleware.py @@ -4,7 +4,6 @@ from unittest.mock import AsyncMock, MagicMock, patch import pytest - from microsoft_agents.activity import ( Activity, ChannelAccount, diff --git a/tests/a365/hosting/scope_helpers/test_populate_baggage.py b/tests/a365/hosting/scope_helpers/test_populate_baggage.py index 46222ddb..4e90f0c4 100644 --- a/tests/a365/hosting/scope_helpers/test_populate_baggage.py +++ b/tests/a365/hosting/scope_helpers/test_populate_baggage.py @@ -9,6 +9,7 @@ from microsoft.opentelemetry.a365.core.middleware.baggage_builder import BaggageBuilder from microsoft.opentelemetry.a365.hosting.scope_helpers.populate_baggage import populate + def test_populate(): """Test populate populates BaggageBuilder from turn context.""" # Create a real activity and turn context diff --git a/tests/a365/hosting/scope_helpers/test_populate_invoke_agent_scope.py b/tests/a365/hosting/scope_helpers/test_populate_invoke_agent_scope.py index b5f69273..102843cc 100644 --- a/tests/a365/hosting/scope_helpers/test_populate_invoke_agent_scope.py +++ b/tests/a365/hosting/scope_helpers/test_populate_invoke_agent_scope.py @@ -5,7 +5,6 @@ from unittest.mock import MagicMock import pytest - from microsoft_agents.activity import Activity, ChannelAccount, ConversationAccount from microsoft_agents.hosting.core import TurnContext from microsoft.opentelemetry.a365.core.agent_details import AgentDetails diff --git a/tests/a365/hosting/token_cache_helpers/test_agent_token_cache.py b/tests/a365/hosting/token_cache_helpers/test_agent_token_cache.py index 7d5748e2..90130699 100644 --- a/tests/a365/hosting/token_cache_helpers/test_agent_token_cache.py +++ b/tests/a365/hosting/token_cache_helpers/test_agent_token_cache.py @@ -7,7 +7,6 @@ from unittest.mock import AsyncMock, MagicMock import pytest - from microsoft_agents.hosting.core.app.oauth.authorization import Authorization from microsoft_agents.hosting.core.turn_context import TurnContext from microsoft.opentelemetry.a365.hosting.token_cache_helpers import (