Skip to content

Commit 11d3260

Browse files
CopilotJimDaly
andauthored
fix: improve AgentNotification type aliases and fix lifecycle routing bugs
- Replace TypeVar-based AgentHandler with concrete TypeAlias using TurnContext/TurnState - Add RouteHandler TypeAlias for the inner route handler, simplifying method return types - Fix runtime bug: on_lifecycle, on_user_created, on_user_workload_onboarding, on_user_deleted all called non-existent self.on_lifecycle_notification; corrected to on_agent_lifecycle_notification - Export RouteHandler from the package __init__ - Add 16 unit tests covering type alias integrity and routing correctness Agent-Logs-Url: https://github.com/microsoft/Agent365-python/sessions/8d7dad6f-d5c2-4565-895a-9ef968ee6d13 Co-authored-by: JimDaly <6353736+JimDaly@users.noreply.github.com>
1 parent 86bf7c1 commit 11d3260

4 files changed

Lines changed: 285 additions & 33 deletions

File tree

libraries/microsoft-agents-a365-notifications/microsoft_agents_a365/notifications/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from .agent_notification import (
1313
AgentHandler,
1414
AgentNotification,
15+
RouteHandler,
1516
)
1617

1718
# Import all models from the models subpackage
@@ -29,6 +30,7 @@
2930
# Main notification handler
3031
"AgentNotification",
3132
"AgentHandler",
33+
"RouteHandler",
3234
# Models and data classes
3335
"AgentNotificationActivity",
3436
"EmailReference",

libraries/microsoft-agents-a365-notifications/microsoft_agents_a365/notifications/agent_notification.py

Lines changed: 24 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from __future__ import annotations
55

66
from collections.abc import Awaitable, Callable, Iterable
7-
from typing import Any, TypeVar
7+
from typing import Any, TypeAlias
88

99
from microsoft_agents.activity import ChannelId
1010
from microsoft_agents.hosting.core import TurnContext
@@ -14,8 +14,12 @@
1414
from .models.agent_notification_activity import AgentNotificationActivity, NotificationTypes
1515
from .models.agent_subchannel import AgentSubChannel
1616

17-
TContext = TypeVar("TContext", bound=TurnContext)
18-
TState = TypeVar("TState", bound=TurnState)
17+
#: Type alias for the route handler function registered with the application.
18+
#:
19+
#: Route handlers are the inner async functions that the application framework calls
20+
#: when a matching notification is received. They accept a :class:`~microsoft_agents.hosting.core.TurnContext`
21+
#: and a :class:`~microsoft_agents.hosting.core.app.state.TurnState`.
22+
RouteHandler: TypeAlias = Callable[[TurnContext, TurnState], Awaitable[None]]
1923

2024
#: Type alias for agent notification handler functions.
2125
#:
@@ -26,7 +30,8 @@
2630
#: Args:
2731
#: context: The turn context for the current conversation turn.
2832
#: state: The application state for the current turn.
29-
#: notification: The typed notification activity with parsed entities.
33+
#: notification: The typed :class:`~microsoft_agents_a365.notifications.AgentNotificationActivity`
34+
#: with parsed entities.
3035
#:
3136
#: Example:
3237
#:
@@ -40,7 +45,9 @@
4045
#: email = notification.email
4146
#: if email:
4247
#: print(f"Processing email: {email.id}")
43-
AgentHandler = Callable[[TContext, TState, AgentNotificationActivity], Awaitable[None]]
48+
AgentHandler: TypeAlias = Callable[
49+
[TurnContext, TurnState, AgentNotificationActivity], Awaitable[None]
50+
]
4451

4552

4653
class AgentNotification:
@@ -220,9 +227,7 @@ def decorator(handler: AgentHandler):
220227

221228
return decorator
222229

223-
def on_email(
224-
self, **kwargs: Any
225-
) -> Callable[[AgentHandler], Callable[[TurnContext, TurnState], Awaitable[None]]]:
230+
def on_email(self, **kwargs: Any) -> Callable[[AgentHandler], RouteHandler]:
226231
"""Register a handler for Outlook email notifications.
227232
228233
This is a convenience decorator that registers a handler for notifications
@@ -251,9 +256,7 @@ async def handle_email(context, state, notification):
251256
ChannelId(channel="agents", sub_channel=AgentSubChannel.EMAIL), **kwargs
252257
)
253258

254-
def on_word(
255-
self, **kwargs: Any
256-
) -> Callable[[AgentHandler], Callable[[TurnContext, TurnState], Awaitable[None]]]:
259+
def on_word(self, **kwargs: Any) -> Callable[[AgentHandler], RouteHandler]:
257260
"""Register a handler for Microsoft Word comment notifications.
258261
259262
This is a convenience decorator that registers a handler for notifications
@@ -278,9 +281,7 @@ async def handle_word_comment(context, state, notification):
278281
ChannelId(channel="agents", sub_channel=AgentSubChannel.WORD), **kwargs
279282
)
280283

281-
def on_excel(
282-
self, **kwargs: Any
283-
) -> Callable[[AgentHandler], Callable[[TurnContext, TurnState], Awaitable[None]]]:
284+
def on_excel(self, **kwargs: Any) -> Callable[[AgentHandler], RouteHandler]:
284285
"""Register a handler for Microsoft Excel comment notifications.
285286
286287
This is a convenience decorator that registers a handler for notifications
@@ -305,9 +306,7 @@ async def handle_excel_comment(context, state, notification):
305306
ChannelId(channel="agents", sub_channel=AgentSubChannel.EXCEL), **kwargs
306307
)
307308

308-
def on_powerpoint(
309-
self, **kwargs: Any
310-
) -> Callable[[AgentHandler], Callable[[TurnContext, TurnState], Awaitable[None]]]:
309+
def on_powerpoint(self, **kwargs: Any) -> Callable[[AgentHandler], RouteHandler]:
311310
"""Register a handler for Microsoft PowerPoint comment notifications.
312311
313312
This is a convenience decorator that registers a handler for notifications
@@ -332,9 +331,7 @@ async def handle_powerpoint_comment(context, state, notification):
332331
ChannelId(channel="agents", sub_channel=AgentSubChannel.POWERPOINT), **kwargs
333332
)
334333

335-
def on_lifecycle(
336-
self, **kwargs: Any
337-
) -> Callable[[AgentHandler], Callable[[TurnContext, TurnState], Awaitable[None]]]:
334+
def on_lifecycle(self, **kwargs: Any) -> Callable[[AgentHandler], RouteHandler]:
338335
"""Register a handler for all agent lifecycle event notifications.
339336
340337
This is a convenience decorator that registers a handler for all lifecycle
@@ -353,11 +350,9 @@ def on_lifecycle(
353350
async def handle_any_lifecycle_event(context, state, notification):
354351
print(f"Lifecycle event type: {notification.notification_type}")
355352
"""
356-
return self.on_lifecycle_notification("*", **kwargs)
353+
return self.on_agent_lifecycle_notification("*", **kwargs)
357354

358-
def on_user_created(
359-
self, **kwargs: Any
360-
) -> Callable[[AgentHandler], Callable[[TurnContext, TurnState], Awaitable[None]]]:
355+
def on_user_created(self, **kwargs: Any) -> Callable[[AgentHandler], RouteHandler]:
361356
"""Register a handler for user creation lifecycle events.
362357
363358
This is a convenience decorator that registers a handler specifically for
@@ -376,11 +371,9 @@ def on_user_created(
376371
async def handle_user_created(context, state, notification):
377372
print("New agentic user identity created")
378373
"""
379-
return self.on_lifecycle_notification(AgentLifecycleEvent.USERCREATED, **kwargs)
374+
return self.on_agent_lifecycle_notification(AgentLifecycleEvent.USERCREATED, **kwargs)
380375

381-
def on_user_workload_onboarding(
382-
self, **kwargs: Any
383-
) -> Callable[[AgentHandler], Callable[[TurnContext, TurnState], Awaitable[None]]]:
376+
def on_user_workload_onboarding(self, **kwargs: Any) -> Callable[[AgentHandler], RouteHandler]:
384377
"""Register a handler for user workload onboarding update events.
385378
386379
This is a convenience decorator that registers a handler for events that occur
@@ -399,13 +392,11 @@ def on_user_workload_onboarding(
399392
async def handle_onboarding_update(context, state, notification):
400393
print("User workload onboarding status updated")
401394
"""
402-
return self.on_lifecycle_notification(
395+
return self.on_agent_lifecycle_notification(
403396
AgentLifecycleEvent.USERWORKLOADONBOARDINGUPDATED, **kwargs
404397
)
405398

406-
def on_user_deleted(
407-
self, **kwargs: Any
408-
) -> Callable[[AgentHandler], Callable[[TurnContext, TurnState], Awaitable[None]]]:
399+
def on_user_deleted(self, **kwargs: Any) -> Callable[[AgentHandler], RouteHandler]:
409400
"""Register a handler for user deletion lifecycle events.
410401
411402
This is a convenience decorator that registers a handler specifically for
@@ -424,7 +415,7 @@ def on_user_deleted(
424415
async def handle_user_deleted(context, state, notification):
425416
print("Agentic user identity deleted")
426417
"""
427-
return self.on_lifecycle_notification(AgentLifecycleEvent.USERDELETED, **kwargs)
418+
return self.on_agent_lifecycle_notification(AgentLifecycleEvent.USERDELETED, **kwargs)
428419

429420
@staticmethod
430421
def _normalize_subchannel(value: str | AgentSubChannel | None) -> str:

tests/notifications/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Copyright (c) Microsoft Corporation.
2+
# Licensed under the MIT License.

0 commit comments

Comments
 (0)