From bd228b3a85ae2f31e163b20c3ca8ff3901533db6 Mon Sep 17 00:00:00 2001 From: Rodrigo Brandao Date: Mon, 3 Aug 2026 11:42:17 -0700 Subject: [PATCH 1/3] Adding Activity.name to AppOnTurn span --- .../hosting/core/app/telemetry/spans.py | 2 ++ .../hosting/core/telemetry/attributes.py | 1 + tests/activity/test_activity.py | 19 ++++++++++--------- tests/activity/test_activity_builders.py | 3 ++- tests/activity/test_conversation_reference.py | 9 +++++---- .../hosting_core/telemetry/test_app_spans.py | 4 +++- 6 files changed, 23 insertions(+), 15 deletions(-) diff --git a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/telemetry/spans.py b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/telemetry/spans.py index 5222b407..192a8e16 100644 --- a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/telemetry/spans.py +++ b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/telemetry/spans.py @@ -49,6 +49,8 @@ def _get_attributes(self) -> AttributeMap: attributes.ACTIVITY_TYPE: self._turn_context.activity.type, attributes.ACTIVITY_ID: self._turn_context.activity.id or attributes.UNKNOWN, + attributes.ACTIVITY_NAME: self._turn_context.activity.name + or attributes.UNKNOWN, } def share(self, route_authorized: bool, route_matched: bool) -> None: diff --git a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/telemetry/attributes.py b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/telemetry/attributes.py index 81b4e322..28b526d4 100644 --- a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/telemetry/attributes.py +++ b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/telemetry/attributes.py @@ -5,6 +5,7 @@ ACTIVITY_CHANNEL_ID = "activity.channel_id" ACTIVITY_ID = "activity.id" ACTIVITY_COUNT = "activities.count" +ACTIVITY_NAME = "activity.name" ACTIVITY_TYPE = "activity.type" AGENTIC_USER_ID = "agentic.user_id" diff --git a/tests/activity/test_activity.py b/tests/activity/test_activity.py index 1fecd29d..e600db17 100644 --- a/tests/activity/test_activity.py +++ b/tests/activity/test_activity.py @@ -18,6 +18,7 @@ Thing, ProductInfo, RoleTypes, + Unset, ) from tests.activity._common.my_channel_data import MyChannelData @@ -249,8 +250,8 @@ def test_create_trace( elif value: assert trace.value_type == type(value).__name__ else: - assert trace.value_type is None - assert trace.label == label + assert trace.value_type is Unset + assert trace.label == (label if label is not None else Unset) assert trace.name == "test" @pytest.mark.parametrize( @@ -277,8 +278,8 @@ def test_can_create_activities(self, activity_type, activity_type_name): assert activity.type == expected_activity_type if expected_activity_type == ActivityTypes.message: - assert activity.attachments is None - assert activity.entities is None + assert activity.attachments is Unset + assert activity.entities is Unset @pytest.mark.parametrize( "name, value_type, value, label", @@ -291,8 +292,8 @@ def test_create_trace_activity(self, name, value_type, value, label): assert activity.type == ActivityTypes.trace assert activity.name == name assert activity.value_type == type(value).__name__ - assert activity.value == value - assert activity.label == label + assert activity.value == (value if value is not None else Unset) + assert activity.label == (label if label is not None else Unset) @pytest.mark.parametrize( "activity_locale, text, create_recipient, create_from, create_reply_locale", @@ -315,19 +316,19 @@ def test_can_create_reply_activity( assert reply.service_url == "ServiceUrl123" assert reply.channel_id == "ChannelId123" assert reply.text == text or reply.text == "" - assert reply.locale == activity_locale or create_reply_locale + assert reply.locale == (create_reply_locale or activity_locale or Unset) if create_recipient: assert reply.from_property.id == "ChannelAccount_Id_2" assert reply.from_property.name == "ChannelAccount_Name_2" else: - assert reply.from_property is None + assert reply.from_property is Unset if create_from: assert reply.recipient.id == "ChannelAccount_Id_1" assert reply.recipient.name == "ChannelAccount_Name_1" else: - assert reply.recipient is None + assert reply.recipient is Unset @pytest.fixture(params=[None, {}, MyChannelData()]) def channel_data(self, request): diff --git a/tests/activity/test_activity_builders.py b/tests/activity/test_activity_builders.py index 11a5cdbd..72340bb7 100644 --- a/tests/activity/test_activity_builders.py +++ b/tests/activity/test_activity_builders.py @@ -15,6 +15,7 @@ Mention, SuggestedActions, TextFormatTypes, + Unset, ) @@ -50,7 +51,7 @@ def test_with_value_without_value_type(self): activity = Activity.create_message_activity().with_value("theValue") assert activity.value == "theValue" - assert activity.value_type is None + assert activity.value_type is Unset def test_with_suggested_actions(self): actions = SuggestedActions( diff --git a/tests/activity/test_conversation_reference.py b/tests/activity/test_conversation_reference.py index bafabc67..2293c73a 100644 --- a/tests/activity/test_conversation_reference.py +++ b/tests/activity/test_conversation_reference.py @@ -1,7 +1,6 @@ from uuid import UUID import pytest -from pydantic import ValidationError from microsoft_agents.activity import ( ActivityEventNames, @@ -9,6 +8,7 @@ ChannelAccount, ConversationAccount, ConversationReference, + Unset, ) @@ -50,9 +50,10 @@ def test_get_continuation_activity_generates_new_id_each_time(): assert first_activity.id != second_activity.id -def test_get_continuation_activity_raises_when_user_is_missing(): +def test_get_continuation_activity_uses_unset_when_user_is_missing(): conversation_reference = _create_conversation_reference(user=None) conversation_reference.user = None - with pytest.raises(ValidationError): - conversation_reference.get_continuation_activity() + continuation_activity = conversation_reference.get_continuation_activity() + + assert continuation_activity.from_property is Unset diff --git a/tests/hosting_core/telemetry/test_app_spans.py b/tests/hosting_core/telemetry/test_app_spans.py index 45ac5eb2..662518c5 100644 --- a/tests/hosting_core/telemetry/test_app_spans.py +++ b/tests/hosting_core/telemetry/test_app_spans.py @@ -48,7 +48,7 @@ def test_app_on_turn_creates_span(test_exporter): def test_app_on_turn_span_attributes(test_exporter): - ctx = _make_context(id="act-1") + ctx = _make_context(id="act-1", name="message-received") with AppOnTurn(ctx): pass @@ -56,6 +56,7 @@ def test_app_on_turn_span_attributes(test_exporter): span = test_exporter.get_finished_spans()[0] assert span.attributes[attributes.ACTIVITY_TYPE] == "message" assert span.attributes[attributes.ACTIVITY_ID] == "act-1" + assert span.attributes[attributes.ACTIVITY_NAME] == "message-received" def test_app_on_turn_span_attributes_missing_id(test_exporter): @@ -67,6 +68,7 @@ def test_app_on_turn_span_attributes_missing_id(test_exporter): span = test_exporter.get_finished_spans()[0] assert span.attributes[attributes.ACTIVITY_TYPE] == "message" assert span.attributes[attributes.ACTIVITY_ID] == attributes.UNKNOWN + assert span.attributes[attributes.ACTIVITY_NAME] == attributes.UNKNOWN def test_app_on_turn_records_turn_metrics(test_exporter, test_metric_reader): From b388e80754be7b6072aa68142be3e7f1a617eeff Mon Sep 17 00:00:00 2001 From: Rodrigo Brandao Date: Mon, 3 Aug 2026 11:57:17 -0700 Subject: [PATCH 2/3] Undoing unintended testing changes for activity --- tests/activity/test_activity.py | 21 +++++++++---------- tests/activity/test_activity_builders.py | 5 ++--- tests/activity/test_conversation_reference.py | 9 ++++---- 3 files changed, 16 insertions(+), 19 deletions(-) diff --git a/tests/activity/test_activity.py b/tests/activity/test_activity.py index e600db17..ce2c41d0 100644 --- a/tests/activity/test_activity.py +++ b/tests/activity/test_activity.py @@ -18,7 +18,6 @@ Thing, ProductInfo, RoleTypes, - Unset, ) from tests.activity._common.my_channel_data import MyChannelData @@ -250,8 +249,8 @@ def test_create_trace( elif value: assert trace.value_type == type(value).__name__ else: - assert trace.value_type is Unset - assert trace.label == (label if label is not None else Unset) + assert trace.value_type is None + assert trace.label == label assert trace.name == "test" @pytest.mark.parametrize( @@ -278,8 +277,8 @@ def test_can_create_activities(self, activity_type, activity_type_name): assert activity.type == expected_activity_type if expected_activity_type == ActivityTypes.message: - assert activity.attachments is Unset - assert activity.entities is Unset + assert activity.attachments is None + assert activity.entities is None @pytest.mark.parametrize( "name, value_type, value, label", @@ -292,8 +291,8 @@ def test_create_trace_activity(self, name, value_type, value, label): assert activity.type == ActivityTypes.trace assert activity.name == name assert activity.value_type == type(value).__name__ - assert activity.value == (value if value is not None else Unset) - assert activity.label == (label if label is not None else Unset) + assert activity.value == value + assert activity.label == label @pytest.mark.parametrize( "activity_locale, text, create_recipient, create_from, create_reply_locale", @@ -316,19 +315,19 @@ def test_can_create_reply_activity( assert reply.service_url == "ServiceUrl123" assert reply.channel_id == "ChannelId123" assert reply.text == text or reply.text == "" - assert reply.locale == (create_reply_locale or activity_locale or Unset) + assert reply.locale == activity_locale or create_reply_locale if create_recipient: assert reply.from_property.id == "ChannelAccount_Id_2" assert reply.from_property.name == "ChannelAccount_Name_2" else: - assert reply.from_property is Unset + assert reply.from_property is None if create_from: assert reply.recipient.id == "ChannelAccount_Id_1" assert reply.recipient.name == "ChannelAccount_Name_1" else: - assert reply.recipient is Unset + assert reply.recipient is None @pytest.fixture(params=[None, {}, MyChannelData()]) def channel_data(self, request): @@ -627,4 +626,4 @@ def test_get_agentic_tenant_id_returns_none_when_no_tenant(self, agentic_role): conversation=ConversationAccount(id="conversation-id"), ) - assert activity.get_agentic_tenant_id() is None + assert activity.get_agentic_tenant_id() is None \ No newline at end of file diff --git a/tests/activity/test_activity_builders.py b/tests/activity/test_activity_builders.py index 72340bb7..5631af80 100644 --- a/tests/activity/test_activity_builders.py +++ b/tests/activity/test_activity_builders.py @@ -15,7 +15,6 @@ Mention, SuggestedActions, TextFormatTypes, - Unset, ) @@ -51,7 +50,7 @@ def test_with_value_without_value_type(self): activity = Activity.create_message_activity().with_value("theValue") assert activity.value == "theValue" - assert activity.value_type is Unset + assert activity.value_type is None def test_with_suggested_actions(self): actions = SuggestedActions( @@ -224,4 +223,4 @@ def test_is_trace_command_predicates(self): assert Activity(type="trace").is_trace() is True assert Activity(type="command").is_command() is True assert Activity(type="commandResult").is_command_result() is True - assert Activity(type="message").is_command() is False + assert Activity(type="message").is_command() is False \ No newline at end of file diff --git a/tests/activity/test_conversation_reference.py b/tests/activity/test_conversation_reference.py index 2293c73a..5f56067b 100644 --- a/tests/activity/test_conversation_reference.py +++ b/tests/activity/test_conversation_reference.py @@ -1,6 +1,7 @@ from uuid import UUID import pytest +from pydantic import ValidationError from microsoft_agents.activity import ( ActivityEventNames, @@ -8,7 +9,6 @@ ChannelAccount, ConversationAccount, ConversationReference, - Unset, ) @@ -50,10 +50,9 @@ def test_get_continuation_activity_generates_new_id_each_time(): assert first_activity.id != second_activity.id -def test_get_continuation_activity_uses_unset_when_user_is_missing(): +def test_get_continuation_activity_raises_when_user_is_missing(): conversation_reference = _create_conversation_reference(user=None) conversation_reference.user = None - continuation_activity = conversation_reference.get_continuation_activity() - - assert continuation_activity.from_property is Unset + with pytest.raises(ValidationError): + conversation_reference.get_continuation_activity() \ No newline at end of file From 5684d3d673d0aacc16a25c55d5544cc6b681d6fd Mon Sep 17 00:00:00 2001 From: Rodrigo Brandao Date: Mon, 3 Aug 2026 11:58:17 -0700 Subject: [PATCH 3/3] Formatting --- tests/activity/test_activity.py | 2 +- tests/activity/test_activity_builders.py | 2 +- tests/activity/test_conversation_reference.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/activity/test_activity.py b/tests/activity/test_activity.py index ce2c41d0..1fecd29d 100644 --- a/tests/activity/test_activity.py +++ b/tests/activity/test_activity.py @@ -626,4 +626,4 @@ def test_get_agentic_tenant_id_returns_none_when_no_tenant(self, agentic_role): conversation=ConversationAccount(id="conversation-id"), ) - assert activity.get_agentic_tenant_id() is None \ No newline at end of file + assert activity.get_agentic_tenant_id() is None diff --git a/tests/activity/test_activity_builders.py b/tests/activity/test_activity_builders.py index 5631af80..11a5cdbd 100644 --- a/tests/activity/test_activity_builders.py +++ b/tests/activity/test_activity_builders.py @@ -223,4 +223,4 @@ def test_is_trace_command_predicates(self): assert Activity(type="trace").is_trace() is True assert Activity(type="command").is_command() is True assert Activity(type="commandResult").is_command_result() is True - assert Activity(type="message").is_command() is False \ No newline at end of file + assert Activity(type="message").is_command() is False diff --git a/tests/activity/test_conversation_reference.py b/tests/activity/test_conversation_reference.py index 5f56067b..bafabc67 100644 --- a/tests/activity/test_conversation_reference.py +++ b/tests/activity/test_conversation_reference.py @@ -55,4 +55,4 @@ def test_get_continuation_activity_raises_when_user_is_missing(): conversation_reference.user = None with pytest.raises(ValidationError): - conversation_reference.get_continuation_activity() \ No newline at end of file + conversation_reference.get_continuation_activity()