Improve dialog opening and card submitting semantics#222
Merged
Conversation
heyitsaamir
added a commit
that referenced
this pull request
Nov 24, 2025
We wrap our AI functions such that we can call the AI plugins etc. But if there are no parameters, we call the function like `foo()`. However, the wrapped function always has 1 positional argument. So python throws. This fixes that bug. #### PR Dependency Tree * **PR #221** 👈 * **PR #222** This tree was auto-generated by [Charcoal](https://github.com/danerwilliams/charcoal)
Contributor
There was a problem hiding this comment.
Pull request overview
This PR introduces cleaner semantics for dialog opening and card action routing by implementing action-based routing patterns. Instead of manually extracting action identifiers from data dictionaries in handler logic, developers can now specify routing identifiers directly in the decorator, improving code clarity and maintainability.
Key Changes
- Introduces
OpenDialogDataandSubmitActionData(aliased fromEnhancedSubmitActionData) utility classes that abstract away the complexity of setting reserved keywords for routing - Enhances
on_dialog_open,on_dialog_submit, andon_card_actiondecorators to accept optional routing identifiers, enabling specific handler matching - Fixes function handler wrapping in
ChatPromptto properly support functions with no parameters when used with plugins
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
packages/cards/src/microsoft/teams/cards/utilities/open_dialog.py |
New utility class for creating dialog open action data with dialog_id routing |
packages/cards/src/microsoft/teams/cards/utilities/submit_dialog.py |
New utility class for creating card submit action data with action routing keyword |
packages/cards/src/microsoft/teams/cards/utilities/__init__.py |
Exports utility classes with convenient naming |
packages/cards/src/microsoft/teams/cards/__init__.py |
Integrates utility classes into main cards package exports |
packages/apps/src/microsoft/teams/apps/routing/activity_handlers.py |
Implements manual handler methods with routing support for dialogs and card actions |
packages/apps/src/microsoft/teams/apps/routing/generated_handlers.py |
Removes auto-generated handlers that are now manually implemented |
packages/apps/src/microsoft/teams/apps/routing/activity_route_configs.py |
Comments out route configs for manually implemented handlers |
packages/apps/tests/test_dialog_routing.py |
Comprehensive tests for dialog routing with and without identifiers |
packages/apps/tests/test_card_action_routing.py |
Comprehensive tests for card action routing with and without identifiers |
packages/ai/src/microsoft/teams/ai/chat_prompt.py |
Fixes function handler wrapping to support parameter-less functions with plugins |
packages/ai/tests/test_chat_prompt.py |
Adds test coverage for parameter-less functions with plugins |
examples/dialogs/src/main.py |
Refactored to use new routing patterns and utility classes |
examples/cards/src/main.py |
Refactored to use action-based routing with separate handlers |
packages/apps/src/microsoft/teams/apps/http_plugin.py |
Minor documentation fix removing unused parameter from example |
heyitsaamir
force-pushed
the
aamirj/dialogs
branch
from
November 24, 2025 19:03
3e81de6 to
5a48dee
Compare
lilyydu
reviewed
Nov 26, 2025
lilyydu
reviewed
Mar 23, 2026
heyitsaamir
commented
Mar 23, 2026
corinagum
reviewed
Mar 24, 2026
corinagum
reviewed
Mar 24, 2026
heyitsaamir
commented
Mar 30, 2026
heyitsaamir
commented
Apr 1, 2026
This was referenced Apr 2, 2026
corinagum
added a commit
to microsoft/teams.ts
that referenced
this pull request
Apr 7, 2026
## Summary
- Adds `OpenDialogData` class that extends `SubmitActionData` and
abstracts away the `msteams: { type: 'task/fetch' }` protocol detail for
opening dialogs. Takes a `dialogId` and optional extra data.
- Adds `SubmitData` class that extends `SubmitActionData` with a
convenience constructor accepting an `action` identifier for handler
routing. Aligns with [web form `action`
attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/form#action).
- Above matches Python SDK implementation (microsoft/teams.py#222).
- Updates dialogs and cards examples to use the new classes.
- Updates dialogs example handler to read `dialog_id` (the reserved
field set by `OpenDialogData`) instead of `opendialogtype`.
- Regenerated core.ts from latest codegen: non-required hero constructor
params now use `?` only when type-safe (arrays, `any`, or types that
include `undefined`). Required hero params stay required.
- New generated types: CollabStageInvokeDataValue, OpenUrlDialogAction,
PopoverAction, ProgressBar, ProgressRing, Resources, StringResource,
TabInfo.
**Note:** Identifier-based handler routing (e.g. `on('dialog.open',
'simple_form')`) is not included in this PR and will be scoped
separately.
## Test plan
- [x] 0 TypeScript compilation errors
- [x] 18/18 cards package tests pass
- [x] E2E: dialogs example tested against Teams (dialog open, form
submit, multi-step)
- [x] E2E: cards example tested against Teams (submit_basic,
save_profile, submit_feedback)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
corinagum
added a commit
to microsoft/teams.net
that referenced
this pull request
Apr 7, 2026
#410) ## Summary - Adds `OpenDialogData` class that extends `SubmitActionData` and abstracts away the `msteams: { type: 'task/fetch' }` protocol detail for opening dialogs. Takes a `dialogId` and optional extra data. - Adds `SubmitData` class that extends `SubmitActionData` with a convenience constructor accepting an `action` identifier for handler routing. Aligns with [web form `action` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/form#action). - Updates dialogs and cards samples to use the new classes. - Updates dialogs sample handler to read `dialog_id` (the reserved field set by `OpenDialogData`) instead of `opendialogtype`. - Regenerated Core.cs from latest codegen: `params IList<T>` replaced with `params T[]` for .NET 8 compatibility, `IList<T>` overloads added for backward compat, parameterless constructors added for System.Text.Json deserialization. - New generated types: CollabStageInvokeDataValue, OpenUrlDialogAction, PopoverAction, ProgressBar, ProgressRing, Resources, StringResource, TabInfo. - Matches Python SDK implementation (microsoft/teams.py#222). **Note:** Identifier-based handler routing is not included in this PR and will be scoped separately. ## Test plan - [x] Build succeeds on .NET 8 and .NET 10 (0 errors) - [x] 588/588 tests pass on .NET 10 - [x] 11/11 cards tests pass on .NET 8 - [x] E2E: dialogs sample tested against Teams on .NET 8 - [x] E2E: cards sample tested against Teams on .NET 8 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR attempts to improve the devex for dialog opening and card submitting semantics.
For opening a dialog, before:
@app.on_message async def handle_message(ctx: ActivityContext[MessageActivity]) -> None: """Handle message activities and show dialog launcher card.""" # Create the launcher adaptive card using Python objects to demonstrate SubmitActionData # This tests that ms_teams correctly serializes to 'msteams' card = AdaptiveCard(version="1.4") card.body = [TextBlock(text="Select the examples you want to see!", size="Large", weight="Bolder")] - # Use SubmitActionData with ms_teams to test serialization - # SubmitActionData uses extra="allow" to accept custom fields - simple_form_data = SubmitActionData.model_validate({"opendialogtype": "simple_form"}) - simple_form_data.ms_teams = TaskFetchSubmitActionData().model_dump() - card.actions = [ - SubmitAction(title="Simple form test").with_data(simple_form_data) - ] + # Use OpenDialogData to create dialog open actions with clean API + card.actions = [ + SubmitAction(title="Simple form test").with_data(OpenDialogData("simple_form")) + ] # Send the card as an attachment message = MessageActivityInput(text="Enter this form").add_card(card) await ctx.send(message) - @app.on_dialog_open + @app.on_dialog_open("simple_form") async def handle_dialog_open(ctx: ActivityContext[TaskFetchInvokeActivity]): """Handle dialog open events for all dialog types.""" - data: Optional[Any] = ctx.activity.value.data - dialog_type = data.get("opendialogtype") if data else None - if dialog_type == "simple_form": dialog_card = AdaptiveCard.model_validate( { "type": "AdaptiveCard", "version": "1.4", "body": [ {"type": "TextBlock", "text": "This is a simple form", "size": "Large", "weight": "Bolder"}, { "type": "Input.Text", "id": "name", "label": "Name", "placeholder": "Enter your name", "isRequired": True, }, ], "actions": [ {"type": "Action.Submit", "title": "Submit", "data": {"submissiondialogtype": "simple_form"}} ], } ) return InvokeResponse( body=TaskModuleResponse( task=TaskModuleContinueResponse( value=CardTaskModuleTaskInfo( title="Simple Form Dialog", card=card_attachment(AdaptiveCardAttachment(content=dialog_card)), ) ) ) ) - # Default return for unknown dialog types - return TaskModuleResponse(task=TaskModuleMessageResponse(value="Unknown dialog type"))Notice how we have the
opendialogtypethat's kind of hidden away in the code. Then, in youron_dialog_openyou have to pull the action out, and then handle it.The two additions are:
on_dialog_openaccepts a dialog identifier to know when to trigger the handler.Now, with submitting cards, we have a similar issue.
@app.on_dialog_open("simple_form") async def handle_dialog_open(ctx: ActivityContext[TaskFetchInvokeActivity]): """Handle dialog open events for all dialog types.""" dialog_card = AdaptiveCard.model_validate( { "type": "AdaptiveCard", "version": "1.4", "body": [ {"type": "TextBlock", "text": "This is a simple form", "size": "Large", "weight": "Bolder"}, { "type": "Input.Text", "id": "name", "label": "Name", "placeholder": "Enter your name", "isRequired": True, }, ], - "actions": [ - {"type": "Action.Submit", "title": "Submit", "data": {"submissiondialogtype": "simple_form"}} - ], + # Alternative: Use SubmitActionData for cleaner action-based routing + # SubmitAction(title="Submit").with_data(SubmitActionData("submit_simple_form")) + {"type": "Action.Submit", "title": "Submit", "data": {"action": "submit_simple_form"}} } ) return InvokeResponse( body=TaskModuleResponse( task=TaskModuleContinueResponse( value=CardTaskModuleTaskInfo( title="Simple Form Dialog", card=card_attachment(AdaptiveCardAttachment(content=dialog_card)), ) ) ) ) # Default return for unknown dialog types return TaskModuleResponse(task=TaskModuleMessageResponse(value="Unknown dialog type")) - @app.on_dialog_submit + @app.on_dialog_submit("submit_simple_form") async def handle_dialog_submit(ctx: ActivityContext[TaskSubmitInvokeActivity]): """Handle dialog submit events for all dialog types.""" data: Optional[Any] = ctx.activity.value.data - dialog_type = data.get("submissiondialogtype") if data else None - if dialog_type == "simple_form": name = data.get("name") if data else None await ctx.send(f"Hi {name}, thanks for submitting the form!") return TaskModuleResponse(task=TaskModuleMessageResponse(value="Form was submitted")) - return TaskModuleResponse(task=TaskModuleMessageResponse(value="Unknown submission type"))We reserve a keyword "action" to indicate what handler needs to be called when the form is submitted. The keyword
actionwas used to align with web standards.This also extends to general card actions too:
profile_card = AdaptiveCard( schema="http://adaptivecards.io/schemas/adaptive-card.json", body=[ TextBlock(text="User Profile", weight="Bolder", size="Large"), TextInput(id="name").with_label("Name").with_value("John Doe"), TextInput(id="email", label="Email", value="[email protected]"), ToggleInput(title="Subscribe to newsletter").with_id("subscribe").with_value("false"), ActionSet( actions=[ ExecuteAction(title="Save") + .with_data(SubmitActionData("save_profile", {"entity_id": "12345"})) .with_associated_inputs("auto"), OpenUrlAction(url="https://adaptivecards.microsoft.com").with_title("Learn More"), ] ), ], ) + @app.on_card_execute_action("save_profile") async def handle_save_profile(ctx: ActivityContext[AdaptiveCardInvokeActivity]) -> AdaptiveCardInvokeResponse: """Handle profile save.""" data = ctx.activity.value.action.data print("Received save_profile action data:", data) entity_id = data.get("entity_id") name = data.get("name", "Unknown") email = data.get("email", "No email") subscribe = data.get("subscribe", "false") age = data.get("age") location = data.get("location", "Not specified") response_text = f"Profile saved!\nName: {name}\nEmail: {email}\nSubscribed: {subscribe}" if entity_id: response_text += f"\nEntity ID: {entity_id}" if age: response_text += f"\nAge: {age}" if location != "Not specified": response_text += f"\nLocation: {location}" await ctx.send(response_text) return AdaptiveCardActionMessageResponse( status_code=200, type="application/vnd.microsoft.activity.message", value="Action processed successfully", )PR Dependency Tree
This tree was auto-generated by Charcoal