AgentApplication.AdaptiveCard support - #518
Conversation
AgentApplication.AdaptiveCard support
There was a problem hiding this comment.
Pull request overview
Adds a new Adaptive Card routing/response surface under microsoft_agents.hosting.core.app intended to support Action.Execute, Action.Submit, and Adaptive Card dynamic search invoke flows, plus supporting response/content-type updates in the activity models.
Changes:
- Introduces an
AdaptiveCardroute registrar (execute/submit/search) and related handler type defs/models. - Adds an Adaptive Card invoke-response factory module for common response shapes and error handling.
- Expands
AdaptiveCardInvokeResponse.valueto accept non-dict payloads and adds additionalContentTypesconstants for invoke responses/errors.
Reviewed changes
Copilot reviewed 8 out of 9 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/adaptive_card/utils.py | Adds (currently commented-out) utility placeholder code related to search invoke validation. |
| libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/adaptive_card/models.py | Adds dataclass models for search query params/results. |
| libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/adaptive_card/factory.py | Adds helpers to construct AdaptiveCardInvokeResponse objects (adaptive card, search, errors, auth). |
| libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/adaptive_card/adaptive_card.py | Adds the Adaptive Card routing registrar (Action.Execute/Submit/Search) and invoke-response sending logic. |
| libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/adaptive_card/adaptive_card_options.py | Adds options type intended to configure Adaptive Card behaviors (e.g., submit filter). |
| libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/adaptive_card/_type_defs.py | Adds protocol handler signatures for adaptive card routes. |
| libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/adaptive_card/init.py | Package initializer (currently empty). |
| libraries/microsoft-agents-activity/microsoft_agents/activity/content_types.py | Adds content-type constants for invoke error/message/login/search responses. |
| libraries/microsoft-agents-activity/microsoft_agents/activity/adaptive_card_invoke_response.py | Broadens value to object to support more response payload shapes. |
Suppressed comments (2)
libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/adaptive_card/factory.py:91
not_supported()currently passes the detailed message as the error "code" and the literal "NotSupported" as the human message, which flipscode/messagein the payload.
def not_supported(message: str) -> AdaptiveCardInvokeResponse:
return error(
HTTPStatus.NOT_IMPLEMENTED,
"NotSupported",
message,
)
libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/adaptive_card/factory.py:99
internal_error()currently passes the detailed message as the error "code" and the literal "InternalError" as the human message, which flipscode/messagein the payload.
def internal_error(message: str) -> AdaptiveCardInvokeResponse:
return error(
HTTPStatus.INTERNAL_SERVER_ERROR,
"InternalError",
message,
)
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 12 out of 13 changed files in this pull request and generated 2 comments.
Suppressed comments (9)
libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/adaptive_card/factory.py:83
- bad_request() is passing arguments to error() in the wrong order, which swaps the intended error "code" and "message" fields in the response body.
def bad_request(message: str) -> AdaptiveCardInvokeResponse:
return error(
HTTPStatus.BAD_REQUEST,
"BadRequest",
message,
)
libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/adaptive_card/factory.py:91
- not_supported() is passing arguments to error() in the wrong order, which swaps the intended error "code" and "message" fields in the response body.
def not_supported(message: str) -> AdaptiveCardInvokeResponse:
return error(
HTTPStatus.NOT_IMPLEMENTED,
"NotSupported",
message,
)
libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/adaptive_card/factory.py:99
- internal_error() is passing arguments to error() in the wrong order, which swaps the intended error "code" and "message" fields in the response body.
def internal_error(message: str) -> AdaptiveCardInvokeResponse:
return error(
HTTPStatus.INTERNAL_SERVER_ERROR,
"InternalError",
message,
)
libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/adaptive_card/adaptive_card.py:116
- Activity.value is typically a dict when parsed from incoming JSON, so getattr(activity.value, submit_filter, None) will not find the submit field and the route selector will never match. Handle dict values explicitly.
verb_value = None
if activity.value is not None:
verb_value = getattr(activity.value, submit_filter, None)
return self._matches(verb, verb_value)
libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/adaptive_card/adaptive_card.py:154
- Activity.value is typically a dict when parsed from incoming JSON, so getattr(activity.value, "dataset", None) will not read the dataset and the search selector will never match. Handle dict values explicitly.
dataset_value = (
getattr(activity.value, "dataset", None)
if activity.value is not None
else None
)
libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/adaptive_card/utils.py:5
- This module is currently entirely commented-out code. Keeping large blocks of commented code makes maintenance harder and tends to drift out of date; either remove the file from the PR or replace it with working utilities (and corresponding tests).
# # Copyright (c) Microsoft Corporation. All rights reserved.
# # Licensed under the MIT License.
# import pydantic
libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/adaptive_card/adaptive_card.py:55
- New AdaptiveCard routing/validation behavior is introduced here, but there are existing route/selector tests for AgentApplication (tests/hosting_core/app/test_agent_application*.py) and no corresponding tests for AdaptiveCard (e.g., selector matching when Activity.value is a dict, and correct invoke_response payloads). Adding tests would help prevent regressions.
def action_execute(
self,
verb: str | Pattern[str],
*,
auth_handlers: list[str] | None = None,
**kwargs,
) -> Callable[[ActionExecuteHandler], ActionExecuteHandler]:
"""Register an ``Action.Execute`` handler that receives the action data."""
libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/adaptive_card/_type_defs.py:4
- TypeVar is imported but never used in this module; this will fail linting in typical configurations. Remove the unused import.
from typing import TypeVar, Awaitable, Protocol
libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/agent_application.py:25
- The PR title suggests
AgentApplication.AdaptiveCardsupport, but AgentApplication currently has no AdaptiveCard registrar/property/method (and no imports or references). As-is, consumers must manually import and instantiate AdaptiveCard, which doesn't match the advertised API surface.
TypeVar,
cast,
overload,
Optional,
)
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 15 out of 15 changed files in this pull request and generated no new comments.
Suppressed comments (4)
libraries/microsoft-agents-activity/microsoft_agents/activity/search_invoke_value.py:21
SearchInvokeValue.kindandquery_textare required, but the hosting-core Adaptive Card search validator contains logic to treat missing/emptykind(Teams fallback) and missingqueryTextas recoverable inputs with a specific error message. With required fields, a missing property will raise validation errors before that logic runs, so the intended behavior can’t be reached.
kind: str
query_text: str
query_options: SearchInvokeOptions
libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/adaptive_card/adaptive_card.py:88
action_executealways sends an InvokeResponse with HTTP status 200 (status_code=HTTPStatus.OK), even when_validate_action_execute_valuereturns an error/not-supported response. Alsoresponse = response or ...won’t set a defaultstatus_codeif the handler returns anAdaptiveCardInvokeResponsewithstatus_code=None, which can lead to missing/incorrect status propagation.
await self._send_invoke_response(
context, response, status_code=HTTPStatus.OK
)
libraries/microsoft-agents-activity/microsoft_agents/activity/adaptive_card_invoke_value.py:24
AdaptiveCardInvokeValuenow requires bothactionandauthentication, but existing tests/handlers expect to be able to validate payloads that omitauthentication(and even omit parts ofaction) and then return a specific adaptive-card error response. Requiring these fields causesmodel_validateto fail early and changes behavior for real incoming activities.
action: AdaptiveCardInvokeAction
authentication: TokenExchangeInvokeRequest
libraries/microsoft-agents-activity/microsoft_agents/activity/adaptive_card_invoke_action.py:29
- Making
idandverbrequired breaks existing usage/tests that construct an Adaptive Card invoke payload with only{"action": {"type": "Action.Execute"}}(seetests/hosting_core/test_activity_handler.py:142). These fields should remain optional to preserve backward compatibility and allow partial payloads during validation/error handling.
type: str
id: str
verb: str
data: dict[NonEmptyString, object] = Field(default_factory=dict)
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 25 out of 25 changed files in this pull request and generated no new comments.
Suppressed comments (2)
libraries/microsoft-agents-activity/microsoft_agents/activity/search_invoke_value.py:21
AdaptiveCard._validate_search_value()attempts to defaultkindto "search" for Teams when it is missing/empty, butSearchInvokeValue.kindis currently required. Ifkindis omitted, Pydantic validation fails earlier and this fallback never runs.
kind: str
query_text: str
query_options: SearchInvokeOptions
libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/adaptive_card/adaptive_card.py:88
Action.Executealways sends an InvokeResponse with HTTP status 200, even when_validate_action_execute_value()returns a 4xx/5xxAdaptiveCardInvokeResponse(e.g., fromfactory.bad_request). Channels typically rely on the outer InvokeResponse status for error handling, so this can mask failures.
await self._send_invoke_response(
context, response, status_code=HTTPStatus.OK
)
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 25 out of 25 changed files in this pull request and generated 1 comment.
Suppressed comments (1)
libraries/microsoft-agents-activity/microsoft_agents/activity/adaptive_card_invoke_value.py:24
AdaptiveCardInvokeValue.actionwas changed to be required, but existing hosting-core code paths still rely on it being optional (e.g.,activity_handler.pychecksif invoke_value.action is None:to produce a specific "Missing action property" error). With the current type, missing/nullactionwill raise validation errors earlier and those branches become unreachable, changing externally visible error behavior.
action: AdaptiveCardInvokeAction
authentication: TokenExchangeInvokeRequest | None = None
Kyle Rohn (kylerohn-msft)
left a comment
There was a problem hiding this comment.
Changelog and description?
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 26 out of 26 changed files in this pull request and generated no new comments.
Suppressed comments (2)
libraries/microsoft-agents-activity/microsoft_agents/activity/search_invoke_value.py:21
SearchInvokeValuedeclareskindandquery_textas required fields, butAdaptiveCard._validate_search_value()explicitly tries to handle missing/empty values (and even auto-fillskindfor Teams). With the current required typing, a missingkind/queryTextwill raise a Pydantic ValidationError and return the generic "not properly formed" bad_request instead of the intended targeted missing-field handling (and the Teams auto-fill path will never execute).
kind: str
query_text: str
query_options: SearchInvokeOptions
libraries/microsoft-agents-activity/microsoft_agents/activity/adaptive_card_invoke_value.py:24
AdaptiveCardInvokeValue.actionis now required, but bothActivityHandler._get_adaptive_card_invoke_value()andAdaptiveCard._validate_action_execute_value()still contain explicitif invoke_value.action is None/ "Missing action property" logic. Withactionrequired, payloads missingactionwill fail validation earlier and return the more generic "Value property is not properly formed" instead of the intended missing-action error handling.
action: AdaptiveCardInvokeAction
authentication: TokenExchangeInvokeRequest | None = None
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 26 out of 26 changed files in this pull request and generated no new comments.
Suppressed comments (3)
libraries/microsoft-agents-activity/microsoft_agents/activity/search_invoke_value.py:21
AdaptiveCard._validate_search_value()tries to accept missingkind(setting it to "search" for Teams) and to produce a targeted "Missing 'kind'" error, butSearchInvokeValuecurrently requireskindandquery_text. That means Pydantic validation fails before this logic runs, so the intended fallback / error messaging is unreachable.
kind: str
query_text: str
query_options: SearchInvokeOptions
libraries/microsoft-agents-activity/microsoft_agents/activity/adaptive_card_invoke_action.py:29
AdaptiveCardInvokeActiondropped theNonEmptyStringconstraint fortype(and also forid/verb). This weakens validation and allows empty strings for fields that are used for routing/behavior, which can lead to hard-to-diagnose failures. Keeping the non-empty constraint preserves the existing type-safety guarantees while still allowingid/verbto be optional.
type: str
id: str | None = None
verb: str | None = None
data: dict[NonEmptyString, object] = Field(default_factory=dict)
tests/hosting_core/app/test_adaptive_card.py:288
- This test hardcodes the search response content-type string even though the PR adds
ContentTypes.search_response. Using the constant keeps the test aligned with the public API and avoids brittle string literals.
assert response.value.body == {
"statusCode": 200,
"type": "application/vnd.microsoft.search.searchResponse",
"value": {"results": [{"title": "Title", "value": "Value"}]},
}
This pull request introduces comprehensive support for Adaptive Card activities, especially focusing on dynamic search and invoke actions, by adding new models, handler interfaces, and registration mechanisms. It also extends content type support and refines several existing data models for better type safety and flexibility.
Key highlights:
Most important changes:
Adaptive Card Search and Invoke Support
SearchInvokeOptions,SearchInvokeValue, andAdaptiveCardSearchInvokeValueto represent search-related invoke payloads, and registered them in__init__.pyfor public API exposure. [1] [2] [3] [4] [5] [6] [7]ActionExecuteHandler,ActionSubmitHandler,SearchHandler) and data models (AdaptiveCardSearchParams,AdaptiveCardSearchResult,Query) for Adaptive Card activity handling in the hosting core. [1] [2]AdaptiveCardclass with registration methods (action_execute,action_submit,search) for routing Adaptive Card actions and search requests, including validation and response composition.factory.pymodule with helper functions to generate standardAdaptiveCardInvokeResponseobjects for various scenarios (success, error, login, etc.).Content Types and Model Improvements
ContentTypeswith new constants for error, message, login request, and search response types, enabling richer and more standardized responses.AdaptiveCardInvokeAction,AdaptiveCardInvokeValue, andAdaptiveCardInvokeResponsemodels for improved type annotations, optional fields, and better default handling using Pydantic'sField. [1] [2] [3] [4]These changes collectively enable robust, extensible handling of Adaptive Card actions and dynamic search scenarios in the agent hosting core.