microsoft-agents-activity 0.5.0.dev5__py3-none-any.whl → 0.5.0.dev10__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of microsoft-agents-activity might be problematic. Click here for more details.

@@ -1,3 +1,6 @@
1
+ # Copyright (c) Microsoft Corporation. All rights reserved.
2
+ # Licensed under the MIT License.
3
+
1
4
  from .agents_model import AgentsModel
2
5
  from .action_types import ActionTypes
3
6
  from .activity import Activity
@@ -17,6 +20,8 @@ from .card_action import CardAction
17
20
  from .card_image import CardImage
18
21
  from .channels import Channels
19
22
  from .channel_account import ChannelAccount
23
+ from ._channel_id_field_mixin import _ChannelIdFieldMixin
24
+ from .channel_id import ChannelId
20
25
  from .conversation_account import ConversationAccount
21
26
  from .conversation_members import ConversationMembers
22
27
  from .conversation_parameters import ConversationParameters
@@ -26,6 +31,7 @@ from .conversations_result import ConversationsResult
26
31
  from .expected_replies import ExpectedReplies
27
32
  from .entity import (
28
33
  Entity,
34
+ EntityTypes,
29
35
  AIEntity,
30
36
  ClientCitation,
31
37
  ClientCitationAppearance,
@@ -36,6 +42,7 @@ from .entity import (
36
42
  SensitivityPattern,
37
43
  GeoCoordinates,
38
44
  Place,
45
+ ProductInfo,
39
46
  Thing,
40
47
  )
41
48
  from .error import Error
@@ -115,6 +122,8 @@ __all__ = [
115
122
  "CardImage",
116
123
  "Channels",
117
124
  "ChannelAccount",
125
+ "ChannelId",
126
+ "_ChannelIdFieldMixin",
118
127
  "ConversationAccount",
119
128
  "ConversationMembers",
120
129
  "ConversationParameters",
@@ -145,6 +154,7 @@ __all__ = [
145
154
  "OAuthCard",
146
155
  "PagedMembersResult",
147
156
  "Place",
157
+ "ProductInfo",
148
158
  "ReceiptCard",
149
159
  "ReceiptItem",
150
160
  "ResourceResponse",
@@ -0,0 +1,96 @@
1
+ # Copyright (c) Microsoft Corporation. All rights reserved.
2
+ # Licensed under the MIT License.
3
+
4
+ from __future__ import annotations
5
+
6
+ import logging
7
+ from typing import Optional, Any
8
+
9
+ from pydantic import (
10
+ ModelWrapValidatorHandler,
11
+ SerializerFunctionWrapHandler,
12
+ computed_field,
13
+ model_validator,
14
+ model_serializer,
15
+ )
16
+
17
+ from .channel_id import ChannelId
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+
22
+ # can be generalized in the future, if needed
23
+ class _ChannelIdFieldMixin:
24
+ """A mixin to add a computed field channel_id of type ChannelId to a Pydantic model."""
25
+
26
+ _channel_id: Optional[ChannelId] = None
27
+
28
+ # required to define the setter below
29
+ @computed_field(return_type=Optional[ChannelId], alias="channelId")
30
+ @property
31
+ def channel_id(self) -> Optional[ChannelId]:
32
+ """Gets the _channel_id field"""
33
+ return self._channel_id
34
+
35
+ # necessary for backward compatibility
36
+ # previously, channel_id was directly assigned with strings
37
+ @channel_id.setter
38
+ def channel_id(self, value: Any):
39
+ """Sets the channel_id after validating it as a ChannelId model."""
40
+ if isinstance(value, ChannelId):
41
+ self._channel_id = value
42
+ elif isinstance(value, str):
43
+ self._channel_id = ChannelId(value)
44
+ else:
45
+ raise ValueError(
46
+ f"Invalid type for channel_id: {type(value)}. "
47
+ "Expected ChannelId or str."
48
+ )
49
+
50
+ def _set_validated_channel_id(self, data: Any) -> None:
51
+ """Sets the channel_id after validating it as a ChannelId model."""
52
+ if "channelId" in data:
53
+ self.channel_id = data["channelId"]
54
+ elif "channel_id" in data:
55
+ self.channel_id = data["channel_id"]
56
+
57
+ @model_validator(mode="wrap")
58
+ @classmethod
59
+ def _validate_channel_id(
60
+ cls, data: Any, handler: ModelWrapValidatorHandler
61
+ ) -> _ChannelIdFieldMixin:
62
+ """Validate the _channel_id field after model initialization.
63
+
64
+ :return: The model instance itself.
65
+ """
66
+ try:
67
+ model = handler(data)
68
+ model._set_validated_channel_id(data)
69
+ return model
70
+ except Exception:
71
+ logging.error("Model %s failed to validate with data %s", cls, data)
72
+ raise
73
+
74
+ def _remove_serialized_unset_channel_id(
75
+ self, serialized: dict[str, object]
76
+ ) -> None:
77
+ """Remove the _channel_id field if it is not set."""
78
+ if not self._channel_id:
79
+ if "channelId" in serialized:
80
+ del serialized["channelId"]
81
+ elif "channel_id" in serialized:
82
+ del serialized["channel_id"]
83
+
84
+ @model_serializer(mode="wrap")
85
+ def _serialize_channel_id(
86
+ self, handler: SerializerFunctionWrapHandler
87
+ ) -> dict[str, object]:
88
+ """Serialize the model using Pydantic's standard serialization.
89
+
90
+ :param handler: The serialization handler provided by Pydantic.
91
+ :return: A dictionary representing the serialized model.
92
+ """
93
+ serialized = handler(self)
94
+ if self: # serialization can be called with None
95
+ self._remove_serialized_unset_channel_id(serialized)
96
+ return serialized
@@ -1,10 +1,24 @@
1
1
  # Copyright (c) Microsoft Corporation. All rights reserved.
2
2
  # Licensed under the MIT License.
3
3
 
4
+ from __future__ import annotations
5
+
6
+ import logging
4
7
  from copy import copy
5
8
  from datetime import datetime, timezone
6
- from typing import Optional
7
- from pydantic import Field, SerializeAsAny
9
+ from typing import Optional, Any
10
+
11
+ from pydantic import (
12
+ Field,
13
+ SerializeAsAny,
14
+ model_serializer,
15
+ model_validator,
16
+ SerializerFunctionWrapHandler,
17
+ ModelWrapValidatorHandler,
18
+ computed_field,
19
+ ValidationError,
20
+ )
21
+
8
22
  from .activity_types import ActivityTypes
9
23
  from .channel_account import ChannelAccount
10
24
  from .conversation_account import ConversationAccount
@@ -14,9 +28,11 @@ from .suggested_actions import SuggestedActions
14
28
  from .attachment import Attachment
15
29
  from .entity import (
16
30
  Entity,
31
+ EntityTypes,
17
32
  Mention,
18
33
  AIEntity,
19
34
  ClientCitation,
35
+ ProductInfo,
20
36
  SensitivityUsageInfo,
21
37
  )
22
38
  from .conversation_reference import ConversationReference
@@ -24,12 +40,16 @@ from .text_highlight import TextHighlight
24
40
  from .semantic_action import SemanticAction
25
41
  from .agents_model import AgentsModel
26
42
  from .role_types import RoleTypes
43
+ from ._channel_id_field_mixin import _ChannelIdFieldMixin
44
+ from .channel_id import ChannelId
27
45
  from ._model_utils import pick_model, SkipNone
28
46
  from ._type_aliases import NonEmptyString
29
47
 
48
+ logger = logging.getLogger(__name__)
49
+
30
50
 
31
51
  # TODO: A2A Agent 2 is responding with None as id, had to mark it as optional (investigate)
32
- class Activity(AgentsModel):
52
+ class Activity(AgentsModel, _ChannelIdFieldMixin):
33
53
  """An Activity is the basic communication type for the protocol.
34
54
 
35
55
  :param type: Contains the activity type. Possible values include:
@@ -50,8 +70,8 @@ class Activity(AgentsModel):
50
70
  :type local_timezone: str
51
71
  :param service_url: Contains the URL that specifies the channel's service endpoint. Set by the channel.
52
72
  :type service_url: str
53
- :param channel_id: Contains an ID that uniquely identifies the channel. Set by the channel.
54
- :type channel_id: str
73
+ :param channel_id: Contains an ID that uniquely identifies the channel (and possibly the sub-channel). Set by the channel.
74
+ :type channel_id: ~microsoft_agents.activity.ChannelId
55
75
  :param from_property: Identifies the sender of the message.
56
76
  :type from_property: ~microsoft_agents.activity.ChannelAccount
57
77
  :param conversation: Identifies the conversation to which the activity belongs.
@@ -136,7 +156,6 @@ class Activity(AgentsModel):
136
156
  local_timestamp: datetime = None
137
157
  local_timezone: NonEmptyString = None
138
158
  service_url: NonEmptyString = None
139
- channel_id: NonEmptyString = None
140
159
  from_property: ChannelAccount = Field(None, alias="from")
141
160
  conversation: ConversationAccount = None
142
161
  recipient: ChannelAccount = None
@@ -173,6 +192,92 @@ class Activity(AgentsModel):
173
192
  semantic_action: SemanticAction = None
174
193
  caller_id: NonEmptyString = None
175
194
 
195
+ @model_validator(mode="wrap")
196
+ @classmethod
197
+ def _validate_channel_id(
198
+ cls, data: Any, handler: ModelWrapValidatorHandler[Activity]
199
+ ) -> Activity:
200
+ """Validate the Activity, ensuring consistency between channel_id.sub_channel and productInfo entity.
201
+
202
+ :param data: The input data to validate.
203
+ :param handler: The validation handler provided by Pydantic.
204
+ :return: The validated Activity instance.
205
+ """
206
+ try:
207
+ # run Pydantic's standard validation first
208
+ activity = handler(data)
209
+
210
+ # needed to assign to a computed field
211
+ # needed because we override the mixin validator
212
+ activity._set_validated_channel_id(data)
213
+
214
+ # sync sub_channel with productInfo entity
215
+ product_info = activity.get_product_info_entity()
216
+ if product_info and activity.channel_id:
217
+ if (
218
+ activity.channel_id.sub_channel
219
+ and activity.channel_id.sub_channel != product_info.id
220
+ ):
221
+ raise Exception(
222
+ "Conflict between channel_id.sub_channel and productInfo entity"
223
+ )
224
+ activity.channel_id = ChannelId(
225
+ channel=activity.channel_id.channel,
226
+ sub_channel=product_info.id,
227
+ )
228
+
229
+ return activity
230
+ except ValidationError as exc:
231
+ logger.error("Validation error for Activity: %s", exc, exc_info=True)
232
+ raise
233
+
234
+ @model_serializer(mode="wrap")
235
+ def _serialize_sub_channel_data(
236
+ self, handler: SerializerFunctionWrapHandler
237
+ ) -> dict[str, object]:
238
+ """Serialize the Activity, ensuring consistency between channel_id.sub_channel and productInfo entity.
239
+
240
+ :param handler: The serialization handler provided by Pydantic.
241
+ :return: A dictionary representing the serialized Activity.
242
+ """
243
+
244
+ # run Pydantic's standard serialization first
245
+ serialized = handler(self)
246
+ if not self: # serialization can be called with None
247
+ return serialized
248
+
249
+ # find the ProductInfo entity
250
+ product_info = None
251
+ for i, entity in enumerate(serialized.get("entities") or []):
252
+ if entity.get("type", "") == EntityTypes.PRODUCT_INFO:
253
+ product_info = entity
254
+ break
255
+
256
+ # maintain consistency between ProductInfo entity and sub channel
257
+ if self.channel_id and self.channel_id.sub_channel:
258
+ if product_info and product_info.get("id") != self.channel_id.sub_channel:
259
+ raise Exception(
260
+ "Conflict between channel_id.sub_channel and productInfo entity"
261
+ )
262
+ elif not product_info:
263
+ if not serialized.get("entities"):
264
+ serialized["entities"] = []
265
+ serialized["entities"].append(
266
+ {
267
+ "type": EntityTypes.PRODUCT_INFO,
268
+ "id": self.channel_id.sub_channel,
269
+ }
270
+ )
271
+ elif product_info: # remove productInfo entity if sub_channel is not set
272
+ del serialized["entities"][i]
273
+ if not serialized["entities"]: # after removal above, list may be empty
274
+ del serialized["entities"]
275
+
276
+ # necessary due to computed_field serialization
277
+ self._remove_serialized_unset_channel_id(serialized)
278
+
279
+ return serialized
280
+
176
281
  def apply_conversation_reference(
177
282
  self, reference: ConversationReference, is_incoming: bool = False
178
283
  ):
@@ -531,6 +636,14 @@ class Activity(AgentsModel):
531
636
  service_url=self.service_url,
532
637
  )
533
638
 
639
+ def get_product_info_entity(self) -> Optional[ProductInfo]:
640
+ if not self.entities:
641
+ return None
642
+ target = EntityTypes.PRODUCT_INFO.lower()
643
+ # validated entities can be Entity, and that prevents us from
644
+ # making assumptions about the casing of the 'type' attribute
645
+ return next(filter(lambda e: e.type.lower() == target, self.entities), None)
646
+
534
647
  def get_mentions(self) -> list[Mention]:
535
648
  """
536
649
  Resolves the mentions from the entities of this activity.
@@ -543,7 +656,7 @@ class Activity(AgentsModel):
543
656
  """
544
657
  if not self.entities:
545
658
  return []
546
- return [x for x in self.entities if x.type.lower() == "mention"]
659
+ return [x for x in self.entities if x.type.lower() == EntityTypes.MENTION]
547
660
 
548
661
  def get_reply_conversation_reference(
549
662
  self, reply: ResourceResponse
@@ -0,0 +1,95 @@
1
+ # Copyright (c) Microsoft Corporation. All rights reserved.
2
+ # Licensed under the MIT License.
3
+
4
+ from __future__ import annotations
5
+
6
+ from typing import Optional, Any
7
+
8
+ from pydantic_core import CoreSchema, core_schema
9
+ from pydantic import GetCoreSchemaHandler
10
+
11
+
12
+ class ChannelId(str):
13
+ """A ChannelId represents a channel and optional sub-channel in the format 'channel:sub_channel'."""
14
+
15
+ def __init__(
16
+ self,
17
+ value: Optional[str] = None,
18
+ *,
19
+ channel: Optional[str] = None,
20
+ sub_channel: Optional[str] = None,
21
+ ) -> None:
22
+ """Initialize a ChannelId instance.
23
+
24
+ :param value: The full channel ID string in the format 'channel:sub_channel'. Must be provided if channel is not provided.
25
+ :param channel: The main channel string. Must be provided if value is not provided.
26
+ :param sub_channel: The sub-channel string.
27
+ :raises ValueError: If the input parameters are invalid. value and channel cannot both be provided.
28
+ """
29
+ super().__init__()
30
+ if not channel:
31
+ split = self.strip().split(":", 1)
32
+ self._channel = split[0].strip()
33
+ self._sub_channel = split[1].strip() if len(split) == 2 else None
34
+ else:
35
+ self._channel = channel
36
+ self._sub_channel = sub_channel
37
+
38
+ def __new__(
39
+ cls,
40
+ value: Optional[str] = None,
41
+ *,
42
+ channel: Optional[str] = None,
43
+ sub_channel: Optional[str] = None,
44
+ ) -> ChannelId:
45
+ """Create a new ChannelId instance.
46
+
47
+ :param value: The full channel ID string in the format 'channel:sub_channel'. Must be provided if channel is not provided.
48
+ :param channel: The main channel string. Must be provided if value is not provided. Must not contain ':', as it delimits channels and sub channels.
49
+ :param sub_channel: The sub-channel string.
50
+ :return: A new ChannelId instance.
51
+ :raises ValueError: If the input parameters are invalid. value and channel cannot both be provided.
52
+ """
53
+ if isinstance(value, str):
54
+ if channel or sub_channel:
55
+ raise ValueError(
56
+ "If value is provided, channel and sub_channel must be None"
57
+ )
58
+
59
+ value = value.strip()
60
+ if value:
61
+ return str.__new__(cls, value)
62
+ raise TypeError("value must be a non empty string if provided")
63
+ else:
64
+ if (
65
+ not isinstance(channel, str)
66
+ or len(channel.strip()) == 0
67
+ or ":" in channel
68
+ ):
69
+ raise TypeError(
70
+ "channel must be a non empty string, and must not contain the ':' character"
71
+ )
72
+ if sub_channel is not None and (not isinstance(sub_channel, str)):
73
+ raise TypeError("sub_channel must be a string if provided")
74
+ channel = channel.strip()
75
+ sub_channel = sub_channel.strip() if sub_channel else None
76
+ if sub_channel:
77
+ return str.__new__(cls, f"{channel}:{sub_channel}")
78
+ return str.__new__(cls, channel)
79
+
80
+ @property
81
+ def channel(self) -> str:
82
+ """The main channel, e.g. 'email' in 'email:work'."""
83
+ return self._channel # type: ignore[return-value]
84
+
85
+ @property
86
+ def sub_channel(self) -> Optional[str]:
87
+ """The sub-channel, e.g. 'work' in 'email:work'. May be None."""
88
+ return self._sub_channel
89
+
90
+ # https://docs.pydantic.dev/dev/concepts/types/#customizing-validation-with-__get_pydantic_core_schema__
91
+ @classmethod
92
+ def __get_pydantic_core_schema__(
93
+ cls, source_type: Any, handler: GetCoreSchemaHandler
94
+ ) -> CoreSchema:
95
+ return core_schema.no_info_after_validator_function(cls, handler(str))
@@ -1,20 +1,27 @@
1
1
  # Copyright (c) Microsoft Corporation. All rights reserved.
2
2
  # Licensed under the MIT License.
3
3
 
4
+ from __future__ import annotations
5
+
4
6
  from uuid import uuid4 as uuid
5
7
  from typing import Optional
8
+ import logging
6
9
 
7
10
  from pydantic import Field
8
11
 
9
12
  from .channel_account import ChannelAccount
13
+ from ._channel_id_field_mixin import _ChannelIdFieldMixin
14
+ from .channel_id import ChannelId
10
15
  from .conversation_account import ConversationAccount
11
16
  from .agents_model import AgentsModel
12
17
  from ._type_aliases import NonEmptyString
13
18
  from .activity_types import ActivityTypes
14
19
  from .activity_event_names import ActivityEventNames
15
20
 
21
+ logger = logging.getLogger(__name__)
22
+
16
23
 
17
- class ConversationReference(AgentsModel):
24
+ class ConversationReference(AgentsModel, _ChannelIdFieldMixin):
18
25
  """An object relating to a particular point in a conversation.
19
26
 
20
27
  :param activity_id: (Optional) ID of the activity to refer to
@@ -26,7 +33,7 @@ class ConversationReference(AgentsModel):
26
33
  :param conversation: Conversation reference
27
34
  :type conversation: ~microsoft_agents.activity.ConversationAccount
28
35
  :param channel_id: Channel ID
29
- :type channel_id: str
36
+ :type channel_id: ~microsoft_agents.activity.ChannelId
30
37
  :param locale: A locale name for the contents of the text field.
31
38
  The locale name is a combination of an ISO 639 two- or three-letter
32
39
  culture code associated with a language and an ISO 3166 two-letter
@@ -43,7 +50,6 @@ class ConversationReference(AgentsModel):
43
50
  user: Optional[ChannelAccount] = None
44
51
  agent: ChannelAccount = Field(None, alias="bot")
45
52
  conversation: ConversationAccount
46
- channel_id: NonEmptyString
47
53
  locale: Optional[NonEmptyString] = None
48
54
  service_url: NonEmptyString = None
49
55
 
@@ -1,5 +1,9 @@
1
+ # Copyright (c) Microsoft Corporation. All rights reserved.
2
+ # Licensed under the MIT License.
3
+
1
4
  from .mention import Mention
2
5
  from .entity import Entity
6
+ from .entity_types import EntityTypes
3
7
  from .ai_entity import (
4
8
  ClientCitation,
5
9
  ClientCitationAppearance,
@@ -11,10 +15,12 @@ from .ai_entity import (
11
15
  )
12
16
  from .geo_coordinates import GeoCoordinates
13
17
  from .place import Place
18
+ from .product_info import ProductInfo
14
19
  from .thing import Thing
15
20
 
16
21
  __all__ = [
17
22
  "Entity",
23
+ "EntityTypes",
18
24
  "AIEntity",
19
25
  "ClientCitation",
20
26
  "ClientCitationAppearance",
@@ -25,5 +31,6 @@ __all__ = [
25
31
  "SensitivityPattern",
26
32
  "GeoCoordinates",
27
33
  "Place",
34
+ "ProductInfo",
28
35
  "Thing",
29
36
  ]
@@ -1,14 +1,12 @@
1
1
  # Copyright (c) Microsoft Corporation. All rights reserved.
2
2
  # Licensed under the MIT License.
3
3
 
4
- from typing import Any, Optional
5
- from enum import Enum
4
+ from typing import Any
6
5
 
7
6
  from pydantic import model_serializer, model_validator
8
7
  from pydantic.alias_generators import to_camel, to_snake
9
8
 
10
9
  from ..agents_model import AgentsModel, ConfigDict
11
- from .._type_aliases import NonEmptyString
12
10
 
13
11
 
14
12
  class Entity(AgentsModel):
@@ -0,0 +1,14 @@
1
+ # Copyright (c) Microsoft Corporation. All rights reserved.
2
+ # Licensed under the MIT License.
3
+
4
+ from enum import Enum
5
+
6
+
7
+ class EntityTypes(str, Enum):
8
+ """Well-known enumeration of entity types."""
9
+
10
+ GEO_COORDINATES = "GeoCoordinates"
11
+ MENTION = "mention"
12
+ PLACE = "Place"
13
+ THING = "Thing"
14
+ PRODUCT_INFO = "ProductInfo"
@@ -1,11 +1,14 @@
1
1
  # Copyright (c) Microsoft Corporation. All rights reserved.
2
2
  # Licensed under the MIT License.
3
3
 
4
- from ..agents_model import AgentsModel
4
+ from typing import Literal
5
+
5
6
  from .._type_aliases import NonEmptyString
7
+ from .entity import Entity
8
+ from .entity_types import EntityTypes
6
9
 
7
10
 
8
- class GeoCoordinates(AgentsModel):
11
+ class GeoCoordinates(Entity):
9
12
  """GeoCoordinates (entity type: "https://schema.org/GeoCoordinates").
10
13
 
11
14
  :param elevation: Elevation of the location [WGS
@@ -26,5 +29,5 @@ class GeoCoordinates(AgentsModel):
26
29
  elevation: float = None
27
30
  latitude: float = None
28
31
  longitude: float = None
29
- type: NonEmptyString = None
32
+ type: Literal[EntityTypes.GEO_COORDINATES] = EntityTypes.GEO_COORDINATES
30
33
  name: NonEmptyString = None
@@ -5,7 +5,7 @@ from typing import Literal
5
5
 
6
6
  from ..channel_account import ChannelAccount
7
7
  from .entity import Entity
8
- from .._type_aliases import NonEmptyString
8
+ from .entity_types import EntityTypes
9
9
 
10
10
 
11
11
  class Mention(Entity):
@@ -21,4 +21,4 @@ class Mention(Entity):
21
21
 
22
22
  mentioned: ChannelAccount = None
23
23
  text: str = None
24
- type: Literal["mention"] = "mention"
24
+ type: Literal[EntityTypes.MENTION] = EntityTypes.MENTION
@@ -1,11 +1,14 @@
1
1
  # Copyright (c) Microsoft Corporation. All rights reserved.
2
2
  # Licensed under the MIT License.
3
3
 
4
- from ..agents_model import AgentsModel
4
+ from typing import Literal
5
+
5
6
  from .._type_aliases import NonEmptyString
7
+ from .entity import Entity
8
+ from .entity_types import EntityTypes
6
9
 
7
10
 
8
- class Place(AgentsModel):
11
+ class Place(Entity):
9
12
  """Place (entity type: "https://schema.org/Place").
10
13
 
11
14
  :param address: Address of the place (may be `string` or complex object of
@@ -26,5 +29,5 @@ class Place(AgentsModel):
26
29
  address: object = None
27
30
  geo: object = None
28
31
  has_map: object = None
29
- type: NonEmptyString = None
32
+ type: Literal[EntityTypes.PLACE] = EntityTypes.PLACE
30
33
  name: NonEmptyString = None
@@ -0,0 +1,20 @@
1
+ # Copyright (c) Microsoft Corporation. All rights reserved.
2
+ # Licensed under the MIT License.
3
+
4
+ from typing import Literal
5
+
6
+ from .entity import Entity
7
+ from .entity_types import EntityTypes
8
+
9
+
10
+ class ProductInfo(Entity):
11
+ """Product information (entity type: "productInfo").
12
+
13
+ :param type: The type of the entity, always "productInfo".
14
+ :type type: str
15
+ :param id: The unique identifier for the product.
16
+ :type id: str
17
+ """
18
+
19
+ type: Literal[EntityTypes.PRODUCT_INFO] = EntityTypes.PRODUCT_INFO
20
+ id: str = None
@@ -1,11 +1,14 @@
1
1
  # Copyright (c) Microsoft Corporation. All rights reserved.
2
2
  # Licensed under the MIT License.
3
3
 
4
- from ..agents_model import AgentsModel
4
+ from typing import Literal
5
+
5
6
  from .._type_aliases import NonEmptyString
7
+ from .entity import Entity
8
+ from .entity_types import EntityTypes
6
9
 
7
10
 
8
- class Thing(AgentsModel):
11
+ class Thing(Entity):
9
12
  """Thing (entity type: "https://schema.org/Thing").
10
13
 
11
14
  :param type: The type of the thing
@@ -14,5 +17,5 @@ class Thing(AgentsModel):
14
17
  :type name: str
15
18
  """
16
19
 
17
- type: NonEmptyString = None
20
+ type: Literal[EntityTypes.THING] = EntityTypes.THING
18
21
  name: NonEmptyString = None
@@ -0,0 +1,150 @@
1
+ Metadata-Version: 2.4
2
+ Name: microsoft-agents-activity
3
+ Version: 0.5.0.dev10
4
+ Summary: A protocol library for Microsoft Agents
5
+ Author: Microsoft Corporation
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/microsoft/Agents
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Operating System :: OS Independent
10
+ Requires-Python: >=3.9
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Requires-Dist: pydantic>=2.10.4
14
+ Dynamic: license-file
15
+
16
+ # Microsoft Agents Activity
17
+
18
+ [![PyPI version](https://img.shields.io/pypi/v/microsoft-agents-activity)](https://pypi.org/project/microsoft-agents-activity/)
19
+
20
+ Core types and schemas for building conversational AI agents that work across Microsoft 365 platforms like Teams, Copilot Studio, and Webchat.
21
+
22
+ # What is this?
23
+
24
+ This library is part of the **Microsoft 365 Agents SDK for Python** - a comprehensive framework for building enterprise-grade conversational AI agents. The SDK enables developers to create intelligent agents that work across multiple platforms including Microsoft Teams, M365 Copilot, Copilot Studio, and web chat, with support for third-party integrations like Slack, Facebook Messenger, and Twilio.
25
+
26
+ ## Packages Overview
27
+
28
+ We offer the following PyPI packages to create conversational experiences based on Agents:
29
+
30
+ | Package Name | PyPI Version | Description |
31
+ |--------------|-------------|-------------|
32
+ | `microsoft-agents-activity` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-activity)](https://pypi.org/project/microsoft-agents-activity/) | Types and validators implementing the Activity protocol spec. |
33
+ | `microsoft-agents-hosting-core` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-hosting-core)](https://pypi.org/project/microsoft-agents-hosting-core/) | Core library for Microsoft Agents hosting. |
34
+ | `microsoft-agents-hosting-aiohttp` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-hosting-aiohttp)](https://pypi.org/project/microsoft-agents-hosting-aiohttp/) | Configures aiohttp to run the Agent. |
35
+ | `microsoft-agents-hosting-teams` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-hosting-teams)](https://pypi.org/project/microsoft-agents-hosting-teams/) | Provides classes to host an Agent for Teams. |
36
+ | `microsoft-agents-storage-blob` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-storage-blob)](https://pypi.org/project/microsoft-agents-storage-blob/) | Extension to use Azure Blob as storage. |
37
+ | `microsoft-agents-storage-cosmos` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-storage-cosmos)](https://pypi.org/project/microsoft-agents-storage-cosmos/) | Extension to use CosmosDB as storage. |
38
+ | `microsoft-agents-authentication-msal` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-authentication-msal)](https://pypi.org/project/microsoft-agents-authentication-msal/) | MSAL-based authentication for Microsoft Agents. |
39
+
40
+ Additionally we provide a Copilot Studio Client, to interact with Agents created in CopilotStudio:
41
+
42
+ | Package Name | PyPI Version | Description |
43
+ |--------------|-------------|-------------|
44
+ | `microsoft-agents-copilotstudio-client` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-copilotstudio-client)](https://pypi.org/project/microsoft-agents-copilotstudio-client/) | Direct to Engine client to interact with Agents created in CopilotStudio |
45
+
46
+ ## Architecture
47
+
48
+ The SDK follows a modular architecture:
49
+ - **Activity Layer**: Protocol definitions for cross-platform messaging
50
+ - **Hosting Layer**: Core agent lifecycle, middleware, and web hosting
51
+ - **Storage Layer**: Persistent state management with Azure backends
52
+ - **Authentication Layer**: Secure identity and token management
53
+ - **Integration Layer**: Platform-specific adapters (Teams, Copilot Studio)
54
+
55
+ ## Installation
56
+
57
+ ```bash
58
+ pip install microsoft-agents-activity
59
+ ```
60
+
61
+ ## Quick Start
62
+ Code below taken from the [Quick Start](https://github.com/microsoft/Agents/tree/main/samples/python/quickstart) sample.
63
+ ```python
64
+ @AGENT_APP.conversation_update("membersAdded")
65
+ async def on_members_added(context: TurnContext, _state: TurnState):
66
+ await context.send_activity(
67
+ "Welcome to the empty agent! "
68
+ "This agent is designed to be a starting point for your own agent development."
69
+ )
70
+ return True
71
+
72
+
73
+ @AGENT_APP.message(re.compile(r"^hello$"))
74
+ async def on_hello(context: TurnContext, _state: TurnState):
75
+ await context.send_activity("Hello!")
76
+
77
+
78
+ @AGENT_APP.activity("message")
79
+ async def on_message(context: TurnContext, _state: TurnState):
80
+ await context.send_activity(f"you said: {context.activity.text}")
81
+ ```
82
+
83
+ ## Common Use Cases
84
+
85
+ ### Rich Messages with Cards
86
+ Code below taken from the [Cards](https://github.com/microsoft/Agents/tree/main/samples/python/cards) sample.
87
+
88
+
89
+ ```python
90
+ @staticmethod
91
+ async def send_animation_card(context: TurnContext):
92
+ card = CardFactory.animation_card(
93
+ AnimationCard(
94
+ title="Microsoft Agents Framework",
95
+ image=ThumbnailUrl(
96
+ url="https://i.giphy.com/Ki55RUbOV5njy.gif", alt="Cute Robot"
97
+ ),
98
+ media=[MediaUrl(url="https://i.giphy.com/Ki55RUbOV5njy.gif")],
99
+ subtitle="Animation Card",
100
+ text="This is an example of an animation card using a gif.",
101
+ aspect="16:9",
102
+ duration="PT2M",
103
+ )
104
+ )
105
+ await CardMessages.send_activity(context, card)
106
+
107
+ @staticmethod
108
+ async def send_activity(context: TurnContext, card: Attachment):
109
+ activity = Activity(type=ActivityTypes.message, attachments=[card])
110
+ await context.send_activity(activity)
111
+ ```
112
+
113
+ ## Activity Types
114
+
115
+ The library supports different types of communication:
116
+
117
+ - **Message** - Regular chat messages with text, cards, attachments
118
+ - **Typing** - Show typing indicators
119
+ - **ConversationUpdate** - People joining/leaving chats
120
+ - **Event** - Custom events and notifications
121
+ - **Invoke** - Direct function calls
122
+ - **EndOfConversation** - End chat sessions
123
+
124
+ ## Key Features
125
+
126
+ ✅ **Type-safe** - Built with Pydantic for automatic validation
127
+ ✅ **Rich content** - Support for cards, images, videos, and interactive elements
128
+ ✅ **Teams ready** - Full Microsoft Teams integration
129
+ ✅ **Cross-platform** - Works across all Microsoft 365 chat platforms
130
+ ✅ **Migration friendly** - Easy upgrade from Bot Framework
131
+
132
+ # Quick Links
133
+
134
+ - 📦 [All SDK Packages on PyPI](https://pypi.org/search/?q=microsoft-agents)
135
+ - 📖 [Complete Documentation](https://aka.ms/agents)
136
+ - 💡 [Python Samples Repository](https://github.com/microsoft/Agents/tree/main/samples/python)
137
+ - 🐛 [Report Issues](https://github.com/microsoft/Agents-for-python/issues)
138
+
139
+ # Sample Applications
140
+ Explore working examples in the [Python samples repository](https://github.com/microsoft/Agents/tree/main/samples/python):
141
+
142
+ |Name|Description|README|
143
+ |----|----|----|
144
+ |Quickstart|Simplest agent|[Quickstart](https://github.com/microsoft/Agents/blob/main/samples/python/quickstart/README.md)|
145
+ |Auto Sign In|Simple OAuth agent using Graph and GitHub|[auto-signin](https://github.com/microsoft/Agents/blob/main/samples/python/auto-signin/README.md)|
146
+ |OBO Authorization|OBO flow to access a Copilot Studio Agent|[obo-authorization](https://github.com/microsoft/Agents/blob/main/samples/python/obo-authorization/README.md)|
147
+ |Semantic Kernel Integration|A weather agent built with Semantic Kernel|[semantic-kernel-multiturn](https://github.com/microsoft/Agents/blob/main/samples/python/semantic-kernel-multiturn/README.md)|
148
+ |Streaming Agent|Streams OpenAI responses|[azure-ai-streaming](https://github.com/microsoft/Agents/blob/main/samples/python/azureai-streaming/README.md)|
149
+ |Copilot Studio Client|Console app to consume a Copilot Studio Agent|[copilotstudio-client](https://github.com/microsoft/Agents/blob/main/samples/python/copilotstudio-client/README.md)|
150
+ |Cards Agent|Agent that uses rich cards to enhance conversation design |[cards](https://github.com/microsoft/Agents/blob/main/samples/python/cards/README.md)|
@@ -1,9 +1,10 @@
1
- microsoft_agents/activity/__init__.py,sha256=YOcBqmggcRMTr1JGEswCBMENn_CW2crgsGQY1dr2Elo,6093
1
+ microsoft_agents/activity/__init__.py,sha256=BYUTF2A_YEw5Ql_hPS7MLBGEsmvey6vrMGh9hpsB7EA,6378
2
+ microsoft_agents/activity/_channel_id_field_mixin.py,sha256=pFMfbN5EVrhO-7mFE-_8kT3yZ1zS6s9jyXoDGTLHT7g,3222
2
3
  microsoft_agents/activity/_load_configuration.py,sha256=ztmv1wOBDE9hUYPPzWexc-qi0eiBmxnay1skJpKVEcY,1125
3
4
  microsoft_agents/activity/_model_utils.py,sha256=gUAKCOl98jA9tt95o5G7LoMuHgROvrW6lY9s0CzQu6k,2004
4
5
  microsoft_agents/activity/_type_aliases.py,sha256=kP5tYxEKSgcgBT3bucdluVVxxopwrUaGqKL7X_TQ0Og,229
5
6
  microsoft_agents/activity/action_types.py,sha256=nslx3hvQAd120NPdXZXdprwdjj3I3d4MWXV1b-Igr_o,419
6
- microsoft_agents/activity/activity.py,sha256=hLtPg9-nt7o0M_jUzkiAw7EfNOlqkAbvvBEDDt-cjCA,28002
7
+ microsoft_agents/activity/activity.py,sha256=fOzNz7wVmgWcbEt2nomXEezshwmiNYQEo64z9kUJM2c,32377
7
8
  microsoft_agents/activity/activity_event_names.py,sha256=dmtE1w50nnemTC_s1QG7IqTbnocpiczK_CG31JpgoJ4,254
8
9
  microsoft_agents/activity/activity_importance.py,sha256=cRIlyz8EjmztPmfcgdqZ6Csp6Vp2rQhXt4ZWIDXdt_s,212
9
10
  microsoft_agents/activity/activity_types.py,sha256=dm4vTVSoa-8_aPY_kZuU6C_FWQAURgZRassR1o8o9qQ,762
@@ -24,12 +25,13 @@ microsoft_agents/activity/card_action.py,sha256=-c9VLUNVrRx1wYCSiCueZRvWOO5nWOA6
24
25
  microsoft_agents/activity/card_image.py,sha256=2DCOErbmGm8dRhJrYhxDweQgH0ys-5_cRISxgJ7hx9k,640
25
26
  microsoft_agents/activity/channel_account.py,sha256=2M5K1NJl1GTttYNI65OQBvxLcgGzx9B7bJhjdTHRsgo,1217
26
27
  microsoft_agents/activity/channel_adapter_protocol.py,sha256=CQU9FhsR5UCzRib8DJVj9DO0131QB9Kn5euhHRKGCf4,2135
28
+ microsoft_agents/activity/channel_id.py,sha256=m59mGBfHQ5g3AdK2p_kMxIm9pC--okgOz3wopTmxFMs,3814
27
29
  microsoft_agents/activity/channels.py,sha256=UC5MDF5C4PbW29rJhtXzfjA-4y4VqPXi1nK8oCmHM0o,4560
28
30
  microsoft_agents/activity/contact_relation_update_action_types.py,sha256=6RIJbnVmr8agTPYYw84xTH9rx88QE0dvMbNwC0cpcG0,208
29
31
  microsoft_agents/activity/conversation_account.py,sha256=fOU9PgxnH8NsHIZDiESU22Ph3oEE0PMnLbKYuz3vgYA,1536
30
32
  microsoft_agents/activity/conversation_members.py,sha256=6-kBKO3W7rtpG1sbhKX5T6FDBDlQDzXdIIpjQALw1K4,552
31
33
  microsoft_agents/activity/conversation_parameters.py,sha256=JwsoZeml90q8W_ngYBao218-q1FIMSAbIf3yUXz3yAg,1475
32
- microsoft_agents/activity/conversation_reference.py,sha256=1Iyx4JeaZOWZvbLS0L1yYwnwRKZASzOASrFXYhyLqEo,2452
34
+ microsoft_agents/activity/conversation_reference.py,sha256=hcWDW8oi18KDX4HCcMfEOcsa9IJgj-_ymXPbg49LGsY,2657
33
35
  microsoft_agents/activity/conversation_resource_response.py,sha256=du_lE0AFPo_PEWj2a5gilsYFD8wfDidPh9LmUHQJVyA,652
34
36
  microsoft_agents/activity/conversation_update_types.py,sha256=l57bE-oXKEYyrTLPmcYCioZD_VSfXQLUl9l29Pkw_7w,568
35
37
  microsoft_agents/activity/conversations_result.py,sha256=GK_l5ou-veMnM7eQgiTQ1eUnf52KKEi6okUzf_ybeNc,617
@@ -78,13 +80,15 @@ microsoft_agents/activity/token_status.py,sha256=o3pGTKRjuqEndRWClMe91Cmox6cW8aD
78
80
  microsoft_agents/activity/transcript.py,sha256=vV0p-JEt62UPp3D4gmz1Fn1vq45vpjc01x2p5iCzKsw,423
79
81
  microsoft_agents/activity/turn_context_protocol.py,sha256=S1lJi_pT-o3w0RNB9p4WDiOEL7SHBf_gojRfY9CBwF4,1787
80
82
  microsoft_agents/activity/video_card.py,sha256=6ZYvTYCsIsACbR3KJBhfwFJ1GTYbjKJWvBsqw0pyyWA,2048
81
- microsoft_agents/activity/entity/__init__.py,sha256=WqUL3e79ifcn8UhHVIHcSqQ6MHaEEyGgKi3v65-jQls,617
83
+ microsoft_agents/activity/entity/__init__.py,sha256=nq9ySwflvNeFWKxZmvb4XenYwB_ATDbeOikkk860UfY,826
82
84
  microsoft_agents/activity/entity/ai_entity.py,sha256=VOjU9Y6m68vkiKUHnqG5XmNPRu4KCSKmd0XPDCJ6B0s,3280
83
- microsoft_agents/activity/entity/entity.py,sha256=AyAigxMH1M966G7gn6BHgjztuaKJLZcdJ5WfyJZsn6A,1180
84
- microsoft_agents/activity/entity/geo_coordinates.py,sha256=N-90kqHX1I04U4rju5WVPMiVaV4EGMfS0bs5C4sX6qM,986
85
- microsoft_agents/activity/entity/mention.py,sha256=1OEmnzNJ9me5dbVwCxjFLFwmj1Ubc9-kDQHuBiuC2Ks,699
86
- microsoft_agents/activity/entity/place.py,sha256=SskI7KzQ8YHCAahOUYDdKnMxWNgLCc76kzPl-DcHfJk,921
87
- microsoft_agents/activity/entity/thing.py,sha256=r2LTZ1Ytp8oau56FaWgniNEszn-TWTP-U-lNb3lS-Mo,453
85
+ microsoft_agents/activity/entity/entity.py,sha256=FmJ23emzoEiV66JjpkOwCQoJReFB_5whihj3PFdRtKk,1105
86
+ microsoft_agents/activity/entity/entity_types.py,sha256=_oqG6O2Ds0BHmY65KN_elaXly6rporMmUYcaWEjqi3s,336
87
+ microsoft_agents/activity/entity/geo_coordinates.py,sha256=xLc8SGGmjmJL3m-ionTicC_rL4x09_0hWa28Ed5JKLs,1080
88
+ microsoft_agents/activity/entity/mention.py,sha256=Gpjy4o7_5EQ1flkADPMzJw6nOU92uzf5Gt54Rj7Oi-c,714
89
+ microsoft_agents/activity/entity/place.py,sha256=uw-YUdbhkgawjor92vZwz7qmdcSIVDhYG0L5CphfFrI,995
90
+ microsoft_agents/activity/entity/product_info.py,sha256=bFacwylgKbLuebM5zVpMyud32SCvH6_pJ4n6_wWYMdg,529
91
+ microsoft_agents/activity/entity/thing.py,sha256=u-uWCHgZrrjh_d7pje1GszNmiDfLRKbM26PJnel8Ye8,527
88
92
  microsoft_agents/activity/teams/__init__.py,sha256=pdcnd_Ud92Eox_fHSgm1kwzwFmG8LVeatF9MRI8IaVY,8532
89
93
  microsoft_agents/activity/teams/app_based_link_query.py,sha256=-2uuPbyElN0DbU5R_mTJ_o7jphBNFr1ZFlLeziXuxn0,462
90
94
  microsoft_agents/activity/teams/batch_failed_entries_response.py,sha256=Q6Mgbdu_OdFGEKmaMu3jr71BSfwNtAgCMLyDSrfLf7I,439
@@ -192,8 +196,8 @@ microsoft_agents/activity/teams/teams_member.py,sha256=vRcLRHy6wTfH7DP6k6QbrKkOR
192
196
  microsoft_agents/activity/teams/teams_paged_members_result.py,sha256=cG4eKHjA0u1EMioC2ErVpydnoPJrtTDcdhdzYET9xto,555
193
197
  microsoft_agents/activity/teams/tenant_info.py,sha256=h8OxMd6LD5lWVj_LK2ut8z7SB0I7Gx74W1wwEiYOzXk,296
194
198
  microsoft_agents/activity/teams/user_meeting_details.py,sha256=tvk2CWYf7Bo-DhK8NSojhanJDXEOGVrKXxJDca03CAo,467
195
- microsoft_agents_activity-0.5.0.dev5.dist-info/licenses/LICENSE,sha256=ws_MuBL-SCEBqPBFl9_FqZkaaydIJmxHrJG2parhU4M,1141
196
- microsoft_agents_activity-0.5.0.dev5.dist-info/METADATA,sha256=wDn9lOzwvLHG2YadGcmwxWIn989eF5e2lWimTdPUGck,430
197
- microsoft_agents_activity-0.5.0.dev5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
198
- microsoft_agents_activity-0.5.0.dev5.dist-info/top_level.txt,sha256=lWKcT4v6fTA_NgsuHdNvuMjSrkiBMXohn64ApY7Xi8A,17
199
- microsoft_agents_activity-0.5.0.dev5.dist-info/RECORD,,
199
+ microsoft_agents_activity-0.5.0.dev10.dist-info/licenses/LICENSE,sha256=ws_MuBL-SCEBqPBFl9_FqZkaaydIJmxHrJG2parhU4M,1141
200
+ microsoft_agents_activity-0.5.0.dev10.dist-info/METADATA,sha256=p9kymyEI4VhcE2XdeZJsbUVe0v4aDMmVBKMQNPKkXHU,8071
201
+ microsoft_agents_activity-0.5.0.dev10.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
202
+ microsoft_agents_activity-0.5.0.dev10.dist-info/top_level.txt,sha256=lWKcT4v6fTA_NgsuHdNvuMjSrkiBMXohn64ApY7Xi8A,17
203
+ microsoft_agents_activity-0.5.0.dev10.dist-info/RECORD,,
@@ -1,13 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: microsoft-agents-activity
3
- Version: 0.5.0.dev5
4
- Summary: A protocol library for Microsoft Agents
5
- Author: Microsoft Corporation
6
- License-Expression: MIT
7
- Project-URL: Homepage, https://github.com/microsoft/Agents
8
- Classifier: Programming Language :: Python :: 3
9
- Classifier: Operating System :: OS Independent
10
- Requires-Python: >=3.9
11
- License-File: LICENSE
12
- Requires-Dist: pydantic>=2.10.4
13
- Dynamic: license-file