Skip to content

Commit a1f388b

Browse files
jsl517claude
andcommitted
Fix GetCallerBaggagePairs: resolve userId across all channels
userId was only set from aad_object_id, which is None on non-Teams channels and A2A calls. Add fallback chain: aad_object_id → agentic_user_id → frm.id Port of microsoft/Agent365-dotnet#246 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 402f822 commit a1f388b

2 files changed

Lines changed: 51 additions & 1 deletion

File tree

  • libraries/microsoft-agents-a365-observability-hosting/microsoft_agents_a365/observability/hosting/scope_helpers
  • tests/observability/hosting/scope_helpers

libraries/microsoft-agents-a365-observability-hosting/microsoft_agents_a365/observability/hosting/scope_helpers/utils.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,10 @@ def get_caller_pairs(activity: Activity) -> Iterator[tuple[str, Any]]:
3636
frm = activity.from_property
3737
if not frm:
3838
return
39-
yield USER_ID_KEY, frm.aad_object_id
39+
# Fallback chain for user_id: AadObjectId → AgenticUserId → From.Id
40+
# AadObjectId is null on non-Teams channels and A2A calls (see dotnet PR #246)
41+
user_id = frm.aad_object_id or frm.agentic_user_id or frm.id
42+
yield USER_ID_KEY, user_id
4043
yield USER_NAME_KEY, frm.name
4144
yield USER_EMAIL_KEY, frm.agentic_user_id
4245

tests/observability/hosting/scope_helpers/test_scope_helper_utils.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,53 @@ def test_get_channel_pairs():
8383
assert (CHANNEL_LINK_KEY, None) in result
8484

8585

86+
def test_get_caller_pairs_non_teams_fallback_to_from_id():
87+
"""Test userId falls back to from.id when aad_object_id is None (non-Teams channel)."""
88+
from_account = ChannelAccount(
89+
id="from-id-123",
90+
name="Non-Teams User",
91+
)
92+
activity = Activity(type="message", from_property=from_account)
93+
94+
result = list(get_caller_pairs(activity))
95+
96+
assert (USER_ID_KEY, "from-id-123") in result
97+
assert (USER_NAME_KEY, "Non-Teams User") in result
98+
assert (USER_EMAIL_KEY, None) in result
99+
100+
101+
def test_get_caller_pairs_a2a_fallback_to_agentic_user_id():
102+
"""Test userId falls back to agentic_user_id for A2A calls (no aad_object_id)."""
103+
from_account = ChannelAccount(
104+
id="from-id-456",
105+
name="Agent Caller",
106+
agentic_user_id="a2a-agent-guid",
107+
)
108+
activity = Activity(type="message", from_property=from_account)
109+
110+
result = list(get_caller_pairs(activity))
111+
112+
assert (USER_ID_KEY, "a2a-agent-guid") in result
113+
assert (USER_EMAIL_KEY, "a2a-agent-guid") in result
114+
115+
116+
def test_get_caller_pairs_aad_object_id_wins_when_all_set():
117+
"""Test aad_object_id takes precedence when all identifiers are present."""
118+
from_account = ChannelAccount(
119+
id="from-id-789",
120+
aad_object_id="aad-wins",
121+
name="Full User",
122+
agentic_user_id="agent-upn",
123+
)
124+
activity = Activity(type="message", from_property=from_account)
125+
126+
result = list(get_caller_pairs(activity))
127+
128+
assert (USER_ID_KEY, "aad-wins") in result
129+
assert (USER_NAME_KEY, "Full User") in result
130+
assert (USER_EMAIL_KEY, "agent-upn") in result
131+
132+
86133
def test_get_conversation_pairs():
87134
"""Test get_conversation_pairs extracts conversation information."""
88135
conversation = ConversationAccount(id="conversation-123")

0 commit comments

Comments
 (0)