-
Notifications
You must be signed in to change notification settings - Fork 12
Simply HttpPlugin to only focus on server-concepts #249
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This pull request simplifies the HttpPlugin by separating activity sending from HTTP transport concerns. The changes adapt the TypeScript PR #424 to Python, introducing a cleaner architectural separation.
Changes:
- Introduces a new
ActivitySenderclass dedicated to sending activities, removing this responsibility fromHttpPlugin - Removes the
Senderplugin interface and related coupling between transport and sending logic - Updates
ActivityContextand other components to useActivitySenderinstead of depending on plugins - Adds
app.initialize()method to enable proactive messaging without starting an HTTP server - Includes a new proactive-messaging example demonstrating serverless message sending
Reviewed changes
Copilot reviewed 22 out of 23 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| uv.lock | Adds proactive-messaging example package to workspace |
| packages/devtools/src/microsoft_teams/devtools/devtools_plugin.py | Updates to use PluginBase instead of Sender, converts Activity to CoreActivity |
| packages/botbuilder/src/microsoft_teams/botbuilder/botbuilder_plugin.py | Updates signature to accept CoreActivity parameter, calls parent with new signature |
| packages/apps/src/microsoft_teams/apps/routing/activity_context.py | Replaces Sender dependency with ActivitySender |
| packages/apps/src/microsoft_teams/apps/plugins/sender.py | Removes the Sender abstract class entirely |
| packages/apps/src/microsoft_teams/apps/plugins/plugin_error_event.py | Removes sender field from error events |
| packages/apps/src/microsoft_teams/apps/plugins/plugin_base.py | Removes send() and create_stream() methods from base plugin |
| packages/apps/src/microsoft_teams/apps/plugins/plugin_activity_sent_event.py | Removes sender field from activity sent events |
| packages/apps/src/microsoft_teams/apps/plugins/plugin_activity_response_event.py | Removes sender field from activity response events |
| packages/apps/src/microsoft_teams/apps/plugins/plugin_activity_event.py | Removes sender field from plugin activity events |
| packages/apps/src/microsoft_teams/apps/plugins/init.py | Removes Sender from exports |
| packages/apps/src/microsoft_teams/apps/http_plugin.py | Removes sending logic, updates to work with CoreActivity, changes to PluginBase |
| packages/apps/src/microsoft_teams/apps/events/types.py | Adds CoreActivity model, removes sender from event dataclasses |
| packages/apps/src/microsoft_teams/apps/events/init.py | Exports CoreActivity |
| packages/apps/src/microsoft_teams/apps/contexts/function_context.py | Uses ActivitySender instead of HttpPlugin for sending |
| packages/apps/src/microsoft_teams/apps/app_process.py | Updates to work without Sender, converts CoreActivity back to Activity |
| packages/apps/src/microsoft_teams/apps/app_plugins.py | Removes Sender dependency from plugin injection |
| packages/apps/src/microsoft_teams/apps/app_events.py | Updates event management to work without sender parameter |
| packages/apps/src/microsoft_teams/apps/app.py | Adds ActivitySender, implements initialize() method, updates send() to use ActivitySender |
| packages/apps/src/microsoft_teams/apps/activity_sender.py | New class handling activity sending and streaming |
| examples/proactive-messaging/src/main.py | Example demonstrating proactive messaging without server |
| examples/proactive-messaging/pyproject.toml | Package configuration for proactive messaging example |
| examples/proactive-messaging/README.md | Documentation for proactive messaging example |
Comments suppressed due to low confidence (1)
packages/apps/src/microsoft_teams/apps/http_plugin.py:317
- The route handler signature has changed from parsing the request body internally to expecting
core_activity: CoreActivityas a parameter. While FastAPI can automatically parse Pydantic models from request bodies, this pattern differs from the manual parsing approach used elsewhere in the codebase (e.g., in the func decorator whereawait r.json()is used).
Please verify that FastAPI correctly injects the CoreActivity parameter when the route is called. If this doesn't work as expected at runtime, consider either:
- Manually parsing the body:
body = await request.json(); core_activity = CoreActivity.model_validate(body) - Using FastAPI's Body annotation:
core_activity: CoreActivity = Body(...)
Note that existing tests likely need updates to match this new signature.
async def on_activity_request(self, core_activity: CoreActivity, request: Request, response: Response) -> Any:
"""Handle incoming Teams activity."""
# Get validated token from middleware (if present - will be missing if skip_auth is True)
if hasattr(request.state, "validated_token") and request.state.validated_token:
token = request.state.validated_token
else:
token = cast(
TokenProtocol,
SimpleNamespace(
app_id="",
app_display_name="",
tenant_id="",
service_url=core_activity.service_url or "",
from_="azure",
from_id="",
is_expired=lambda: False,
),
)
activity_type = core_activity.type or "unknown"
activity_id = core_activity.id or "unknown"
self.logger.debug(f"Received activity: {activity_type} (ID: {activity_id})")
self.logger.debug(f"Processing activity {activity_id} via handler...")
# Process the activity
result = await self._process_activity(core_activity, token)
return self._handle_activity_response(response, result)
def _setup_routes(self) -> None:
"""Setup FastAPI routes."""
self.app.post("/api/messages")(self.on_activity_request)
| __all__ = [ | ||
| "PluginBase", | ||
| "Sender", | ||
| "StreamerProtocol", |
Copilot
AI
Jan 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The removal of Sender from the exports represents a breaking API change. External consumers importing Sender from microsoft_teams.apps will encounter import errors.
Additionally, the removal of the sender field from various event types (PluginErrorEvent, PluginActivitySentEvent, PluginActivityResponseEvent, etc.) and the removal of send() and create_stream() methods from PluginBase are breaking changes.
While these changes align with the architectural goals of separating concerns, consider documenting these breaking changes in release notes or providing a migration guide for users upgrading to this version.
| from microsoft_teams.api.activities import SentActivity | ||
| from microsoft_teams.api.models import ConversationReference | ||
|
|
||
| if TYPE_CHECKING: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice! we get to remove these too lol
| ), | ||
| ) | ||
|
|
||
| activity_type = core_activity.type or "unknown" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if we hit one of these lines, should we be logging an error instead?
| recipient=mock_account, | ||
| ), | ||
| ) | ||
| from microsoft_teams.apps.events import CoreActivity |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we add this to top level?
This is a python adaptation of microsoft/teams.ts#424.
Separate activity sending from HTTP transport layer
The previous architecture tightly coupled HTTP transport concerns with activity sending logic:
Previous Architecture:
There are a few issues with this:
New Architecture
In this PR, I am mainly decoupling responsibilities of HttpPlugin from being BOTH a listener AND a sender, to being just a listener. The sender bit is now separated to a different
ActivitySenderclass. Other than better code organization, the main thing this lets us do is not require the app to run to be able to send proactive messages. This is a huge plus point because now the App can be used in scenarios where it doesn't necessarily need to listen to incoming messages (like agentic notifications!)