microsoft-agents-hosting-dialogs 0.10.0.dev2__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.
Files changed (91) hide show
  1. microsoft_agents/hosting/dialogs/__init__.py +76 -0
  2. microsoft_agents/hosting/dialogs/_component_registration.py +30 -0
  3. microsoft_agents/hosting/dialogs/_telemetry_client.py +78 -0
  4. microsoft_agents/hosting/dialogs/choices/__init__.py +38 -0
  5. microsoft_agents/hosting/dialogs/choices/channel.py +121 -0
  6. microsoft_agents/hosting/dialogs/choices/choice_factory.py +262 -0
  7. microsoft_agents/hosting/dialogs/choices/choice_recognizer.py +148 -0
  8. microsoft_agents/hosting/dialogs/choices/find.py +242 -0
  9. microsoft_agents/hosting/dialogs/choices/models/__init__.py +23 -0
  10. microsoft_agents/hosting/dialogs/choices/models/choice.py +14 -0
  11. microsoft_agents/hosting/dialogs/choices/models/choice_factory_options.py +13 -0
  12. microsoft_agents/hosting/dialogs/choices/models/find_choices_options.py +28 -0
  13. microsoft_agents/hosting/dialogs/choices/models/find_values_options.py +31 -0
  14. microsoft_agents/hosting/dialogs/choices/models/found_choice.py +22 -0
  15. microsoft_agents/hosting/dialogs/choices/models/found_value.py +20 -0
  16. microsoft_agents/hosting/dialogs/choices/models/list_style.py +15 -0
  17. microsoft_agents/hosting/dialogs/choices/models/model_result.py +16 -0
  18. microsoft_agents/hosting/dialogs/choices/models/sorted_value.py +16 -0
  19. microsoft_agents/hosting/dialogs/choices/models/token.py +20 -0
  20. microsoft_agents/hosting/dialogs/choices/tokenizer.py +92 -0
  21. microsoft_agents/hosting/dialogs/component_dialog.py +284 -0
  22. microsoft_agents/hosting/dialogs/dialog.py +198 -0
  23. microsoft_agents/hosting/dialogs/dialog_component_registration.py +52 -0
  24. microsoft_agents/hosting/dialogs/dialog_container.py +31 -0
  25. microsoft_agents/hosting/dialogs/dialog_context.py +426 -0
  26. microsoft_agents/hosting/dialogs/dialog_extensions.py +201 -0
  27. microsoft_agents/hosting/dialogs/dialog_manager.py +189 -0
  28. microsoft_agents/hosting/dialogs/dialog_manager_result.py +17 -0
  29. microsoft_agents/hosting/dialogs/dialog_set.py +174 -0
  30. microsoft_agents/hosting/dialogs/dialog_state.py +20 -0
  31. microsoft_agents/hosting/dialogs/memory/__init__.py +24 -0
  32. microsoft_agents/hosting/dialogs/memory/component_memory_scopes_base.py +14 -0
  33. microsoft_agents/hosting/dialogs/memory/component_path_resolvers_base.py +15 -0
  34. microsoft_agents/hosting/dialogs/memory/dialog_path.py +33 -0
  35. microsoft_agents/hosting/dialogs/memory/dialog_state_manager.py +563 -0
  36. microsoft_agents/hosting/dialogs/memory/dialog_state_manager_configuration.py +11 -0
  37. microsoft_agents/hosting/dialogs/memory/path_resolver_base.py +8 -0
  38. microsoft_agents/hosting/dialogs/memory/path_resolvers/__init__.py +19 -0
  39. microsoft_agents/hosting/dialogs/memory/path_resolvers/alias_path_resolver.py +53 -0
  40. microsoft_agents/hosting/dialogs/memory/path_resolvers/at_at_path_resolver.py +9 -0
  41. microsoft_agents/hosting/dialogs/memory/path_resolvers/at_path_resolver.py +44 -0
  42. microsoft_agents/hosting/dialogs/memory/path_resolvers/dollar_path_resolver.py +9 -0
  43. microsoft_agents/hosting/dialogs/memory/path_resolvers/hash_path_resolver.py +9 -0
  44. microsoft_agents/hosting/dialogs/memory/path_resolvers/percent_path_resolver.py +9 -0
  45. microsoft_agents/hosting/dialogs/memory/scope_path.py +38 -0
  46. microsoft_agents/hosting/dialogs/memory/scopes/__init__.py +31 -0
  47. microsoft_agents/hosting/dialogs/memory/scopes/bot_state_memory_scope.py +66 -0
  48. microsoft_agents/hosting/dialogs/memory/scopes/class_memory_scope.py +64 -0
  49. microsoft_agents/hosting/dialogs/memory/scopes/conversation_memory_scope.py +12 -0
  50. microsoft_agents/hosting/dialogs/memory/scopes/dialog_class_memory_scope.py +52 -0
  51. microsoft_agents/hosting/dialogs/memory/scopes/dialog_context_memory_scope.py +68 -0
  52. microsoft_agents/hosting/dialogs/memory/scopes/dialog_memory_scope.py +75 -0
  53. microsoft_agents/hosting/dialogs/memory/scopes/memory_scope.py +91 -0
  54. microsoft_agents/hosting/dialogs/memory/scopes/settings_memory_scope.py +38 -0
  55. microsoft_agents/hosting/dialogs/memory/scopes/this_memory_scope.py +36 -0
  56. microsoft_agents/hosting/dialogs/memory/scopes/turn_memory_scope.py +86 -0
  57. microsoft_agents/hosting/dialogs/memory/scopes/user_memory_scope.py +12 -0
  58. microsoft_agents/hosting/dialogs/models/__init__.py +15 -0
  59. microsoft_agents/hosting/dialogs/models/dialog_event.py +13 -0
  60. microsoft_agents/hosting/dialogs/models/dialog_events.py +12 -0
  61. microsoft_agents/hosting/dialogs/models/dialog_instance.py +28 -0
  62. microsoft_agents/hosting/dialogs/models/dialog_reason.py +34 -0
  63. microsoft_agents/hosting/dialogs/models/dialog_turn_result.py +17 -0
  64. microsoft_agents/hosting/dialogs/models/dialog_turn_status.py +26 -0
  65. microsoft_agents/hosting/dialogs/object_path.py +315 -0
  66. microsoft_agents/hosting/dialogs/persisted_state.py +22 -0
  67. microsoft_agents/hosting/dialogs/persisted_state_keys.py +8 -0
  68. microsoft_agents/hosting/dialogs/prompts/__init__.py +41 -0
  69. microsoft_agents/hosting/dialogs/prompts/activity_prompt.py +203 -0
  70. microsoft_agents/hosting/dialogs/prompts/attachment_prompt.py +87 -0
  71. microsoft_agents/hosting/dialogs/prompts/choice_prompt.py +156 -0
  72. microsoft_agents/hosting/dialogs/prompts/confirm_prompt.py +161 -0
  73. microsoft_agents/hosting/dialogs/prompts/datetime_prompt.py +90 -0
  74. microsoft_agents/hosting/dialogs/prompts/datetime_resolution.py +16 -0
  75. microsoft_agents/hosting/dialogs/prompts/number_prompt.py +81 -0
  76. microsoft_agents/hosting/dialogs/prompts/oauth_prompt.py +569 -0
  77. microsoft_agents/hosting/dialogs/prompts/oauth_prompt_settings.py +43 -0
  78. microsoft_agents/hosting/dialogs/prompts/prompt.py +224 -0
  79. microsoft_agents/hosting/dialogs/prompts/prompt_culture_models.py +222 -0
  80. microsoft_agents/hosting/dialogs/prompts/prompt_options.py +42 -0
  81. microsoft_agents/hosting/dialogs/prompts/prompt_recognizer_result.py +11 -0
  82. microsoft_agents/hosting/dialogs/prompts/prompt_validator.py +0 -0
  83. microsoft_agents/hosting/dialogs/prompts/prompt_validator_context.py +44 -0
  84. microsoft_agents/hosting/dialogs/prompts/text_prompt.py +82 -0
  85. microsoft_agents/hosting/dialogs/waterfall_dialog.py +266 -0
  86. microsoft_agents/hosting/dialogs/waterfall_step_context.py +109 -0
  87. microsoft_agents_hosting_dialogs-0.10.0.dev2.dist-info/METADATA +87 -0
  88. microsoft_agents_hosting_dialogs-0.10.0.dev2.dist-info/RECORD +91 -0
  89. microsoft_agents_hosting_dialogs-0.10.0.dev2.dist-info/WHEEL +5 -0
  90. microsoft_agents_hosting_dialogs-0.10.0.dev2.dist-info/licenses/LICENSE +21 -0
  91. microsoft_agents_hosting_dialogs-0.10.0.dev2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,86 @@
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 TYPE_CHECKING
7
+
8
+ if TYPE_CHECKING:
9
+ from ...dialog_context import DialogContext
10
+
11
+ from microsoft_agents.hosting.dialogs.memory import scope_path
12
+
13
+ from .memory_scope import MemoryScope
14
+
15
+
16
+ class CaseInsensitiveDict(dict):
17
+ # pylint: disable=protected-access
18
+
19
+ @classmethod
20
+ def _k(cls, key):
21
+ return key.lower() if isinstance(key, str) else key
22
+
23
+ def __init__(self, *args, **kwargs):
24
+ super(CaseInsensitiveDict, self).__init__(*args, **kwargs)
25
+ self._convert_keys()
26
+
27
+ def __getitem__(self, key):
28
+ return super(CaseInsensitiveDict, self).__getitem__(self.__class__._k(key))
29
+
30
+ def __setitem__(self, key, value):
31
+ super(CaseInsensitiveDict, self).__setitem__(self.__class__._k(key), value)
32
+
33
+ def __delitem__(self, key):
34
+ return super(CaseInsensitiveDict, self).__delitem__(self.__class__._k(key))
35
+
36
+ def __contains__(self, key):
37
+ return super(CaseInsensitiveDict, self).__contains__(self.__class__._k(key))
38
+
39
+ def pop(self, key, *args, **kwargs):
40
+ return super(CaseInsensitiveDict, self).pop(
41
+ self.__class__._k(key), *args, **kwargs
42
+ )
43
+
44
+ def get(self, key, *args, **kwargs):
45
+ return super(CaseInsensitiveDict, self).get(
46
+ self.__class__._k(key), *args, **kwargs
47
+ )
48
+
49
+ def setdefault(self, key, *args, **kwargs):
50
+ return super(CaseInsensitiveDict, self).setdefault(
51
+ self.__class__._k(key), *args, **kwargs
52
+ )
53
+
54
+ def update(self, e=None, **f):
55
+ if e is None:
56
+ e = {}
57
+ super(CaseInsensitiveDict, self).update(self.__class__(e))
58
+ super(CaseInsensitiveDict, self).update(self.__class__(**f))
59
+
60
+ def _convert_keys(self):
61
+ for k in list(self.keys()):
62
+ val = super(CaseInsensitiveDict, self).pop(k)
63
+ self.__setitem__(k, val)
64
+
65
+
66
+ class TurnMemoryScope(MemoryScope):
67
+ def __init__(self):
68
+ super().__init__(scope_path.TURN, False)
69
+
70
+ def get_memory(self, dialog_context: "DialogContext") -> object:
71
+ if not dialog_context:
72
+ raise TypeError(f"Expecting: DialogContext, but received None")
73
+
74
+ turn_value = dialog_context.context.turn_state.get(scope_path.TURN, None)
75
+
76
+ if turn_value is None:
77
+ turn_value = CaseInsensitiveDict()
78
+ dialog_context.context.turn_state[scope_path.TURN] = turn_value
79
+
80
+ return turn_value
81
+
82
+ def set_memory(self, dialog_context: "DialogContext", memory: object):
83
+ if not dialog_context:
84
+ raise TypeError(f"Expecting: DialogContext, but received None")
85
+
86
+ dialog_context.context.turn_state[scope_path.TURN] = memory
@@ -0,0 +1,12 @@
1
+ # Copyright (c) Microsoft Corporation. All rights reserved.
2
+ # Licensed under the MIT License.
3
+
4
+ from microsoft_agents.hosting.core import UserState
5
+
6
+ from microsoft_agents.hosting.dialogs.memory import scope_path
7
+ from .bot_state_memory_scope import BotStateMemoryScope
8
+
9
+
10
+ class UserMemoryScope(BotStateMemoryScope):
11
+ def __init__(self):
12
+ super().__init__(UserState, scope_path.USER)
@@ -0,0 +1,15 @@
1
+ from .dialog_event import DialogEvent
2
+ from .dialog_events import DialogEvents
3
+ from .dialog_instance import DialogInstance
4
+ from .dialog_reason import DialogReason
5
+ from .dialog_turn_result import DialogTurnResult
6
+ from .dialog_turn_status import DialogTurnStatus
7
+
8
+ __all__ = [
9
+ "DialogEvent",
10
+ "DialogEvents",
11
+ "DialogInstance",
12
+ "DialogReason",
13
+ "DialogTurnResult",
14
+ "DialogTurnStatus",
15
+ ]
@@ -0,0 +1,13 @@
1
+ # Copyright (c) Microsoft Corporation. All rights reserved.
2
+ # Licensed under the MIT License.
3
+
4
+ from dataclasses import dataclass
5
+ from typing import Any
6
+
7
+
8
+ @dataclass
9
+ class DialogEvent:
10
+
11
+ bubble: bool = False
12
+ name: str = ""
13
+ value: Any = None
@@ -0,0 +1,12 @@
1
+ # Copyright (c) Microsoft Corporation. All rights reserved.
2
+ # Licensed under the MIT License.
3
+
4
+
5
+ class DialogEvents:
6
+ begin_dialog = "beginDialog"
7
+ reprompt_dialog = "repromptDialog"
8
+ cancel_dialog = "cancelDialog"
9
+ error = "error"
10
+ activity_received = "activityReceived"
11
+ recognize_utterance = "recognizeUtterance"
12
+ version_changed = "versionChanged"
@@ -0,0 +1,28 @@
1
+ # Copyright (c) Microsoft Corporation. All rights reserved.
2
+ # Licensed under the MIT License.
3
+
4
+ from dataclasses import dataclass, field
5
+ from typing import Any
6
+
7
+
8
+ @dataclass
9
+ class DialogInstance:
10
+ """
11
+ Tracking information for a dialog on the stack.
12
+ """
13
+
14
+ id: str | None = None
15
+ state: dict[str, Any] = field(default_factory=dict)
16
+
17
+ def __str__(self):
18
+ """
19
+ Gets or sets a stack index.
20
+
21
+ :return: Returns stack index.
22
+ :rtype: str
23
+ """
24
+ result = "\ndialog_instance_id: %s\n" % self.id
25
+ if self.state is not None:
26
+ for key, value in self.state.items():
27
+ result += " {} ({})\n".format(key, str(value))
28
+ return result
@@ -0,0 +1,34 @@
1
+ # Copyright (c) Microsoft Corporation. All rights reserved.
2
+ # Licensed under the MIT License.
3
+ from enum import Enum
4
+
5
+
6
+ class DialogReason(Enum):
7
+ """
8
+ Indicates in which a dialog-related method is being called.
9
+
10
+ :var BeginCalled: A dialog is being started through a call to :meth:`DialogContext.begin()`.
11
+ :vartype BeginCalled: int
12
+ :var ContinueCalled: A dialog is being continued through a call to :meth:`DialogContext.continue_dialog()`.
13
+ :vartype ContinueCalled: int
14
+ :var EndCalled: A dialog ended normally through a call to :meth:`DialogContext.end_dialog()
15
+ :vartype EndCalled: int
16
+ :var ReplaceCalled: A dialog is ending and replaced through a call to :meth:``DialogContext.replace_dialog()`.
17
+ :vartype ReplacedCalled: int
18
+ :var CancelCalled: A dialog was cancelled as part of a call to :meth:`DialogContext.cancel_all_dialogs()`.
19
+ :vartype CancelCalled: int
20
+ :var NextCalled: A preceding step was skipped through a call to :meth:`WaterfallStepContext.next()`.
21
+ :vartype NextCalled: int
22
+ """
23
+
24
+ BeginCalled = 1
25
+
26
+ ContinueCalled = 2
27
+
28
+ EndCalled = 3
29
+
30
+ ReplaceCalled = 4
31
+
32
+ CancelCalled = 5
33
+
34
+ NextCalled = 6
@@ -0,0 +1,17 @@
1
+ # Copyright (c) Microsoft Corporation. All rights reserved.
2
+ # Licensed under the MIT License.
3
+
4
+ from dataclasses import dataclass
5
+ from typing import Any
6
+
7
+ from .dialog_turn_status import DialogTurnStatus
8
+
9
+
10
+ @dataclass
11
+ class DialogTurnResult:
12
+ """
13
+ Result returned to the caller of one of the various stack manipulation methods.
14
+ """
15
+
16
+ status: DialogTurnStatus
17
+ result: Any = None
@@ -0,0 +1,26 @@
1
+ # Copyright (c) Microsoft Corporation. All rights reserved.
2
+ # Licensed under the MIT License.
3
+ from enum import Enum
4
+
5
+
6
+ class DialogTurnStatus(Enum):
7
+ """
8
+ Indicates in which a dialog-related method is being called.
9
+
10
+ :var Empty: Indicates that there is currently nothing on the dialog stack.
11
+ :vartype Empty: int
12
+ :var Waiting: Indicates that the dialog on top is waiting for a response from the user.
13
+ :vartype Waiting: int
14
+ :var Complete: Indicates that the dialog completed successfully, the result is available, and the stack is empty.
15
+ :vartype Complete: int
16
+ :var Cancelled: Indicates that the dialog was cancelled and the stack is empty.
17
+ :vartype Cancelled: int
18
+ """
19
+
20
+ Empty = 1
21
+
22
+ Waiting = 2
23
+
24
+ Complete = 3
25
+
26
+ Cancelled = 4
@@ -0,0 +1,315 @@
1
+ # Copyright (c) Microsoft Corporation. All rights reserved.
2
+ # Licensed under the MIT License.
3
+
4
+ import copy
5
+ from typing import Union, Callable
6
+
7
+
8
+ class ObjectPath:
9
+ """
10
+ Helper methods for working with json objects.
11
+ """
12
+
13
+ @staticmethod
14
+ def assign(start_object, overlay_object, default: Union[Callable, object] = None):
15
+ """
16
+ Creates a new object by overlaying values in start_object with non-null values from overlay_object.
17
+
18
+ :param start_object: dict or typed object, the target object to set values on
19
+ :param overlay_object: dict or typed object, the item to overlay values form
20
+ :param default: Provides a default object if both source and overlay are None
21
+ :return: A copy of start_object, with values from overlay_object
22
+ """
23
+ if start_object and overlay_object:
24
+ merged = copy.deepcopy(start_object)
25
+
26
+ def merge(target: dict, source: dict):
27
+ key_set = set(target).union(set(source))
28
+
29
+ for key in key_set:
30
+ target_value = target.get(key)
31
+ source_value = source.get(key)
32
+
33
+ # skip empty overlay items
34
+ if source_value:
35
+ if isinstance(source_value, dict):
36
+ # merge dictionaries
37
+ if not target_value:
38
+ target[key] = copy.deepcopy(source_value)
39
+ else:
40
+ merge(target_value, source_value)
41
+ elif not hasattr(source_value, "__dict__"):
42
+ # simple type. just copy it.
43
+ target[key] = copy.copy(source_value)
44
+ elif not target_value:
45
+ # the target doesn't have the value, but
46
+ # the overlay does. just copy it.
47
+ target[key] = copy.deepcopy(source_value)
48
+ else:
49
+ # recursive class copy
50
+ merge(target_value.__dict__, source_value.__dict__)
51
+
52
+ target_dict = merged if isinstance(merged, dict) else merged.__dict__
53
+ overlay_dict = (
54
+ overlay_object
55
+ if isinstance(overlay_object, dict)
56
+ else overlay_object.__dict__
57
+ )
58
+ merge(target_dict, overlay_dict)
59
+
60
+ return merged
61
+
62
+ if overlay_object:
63
+ return copy.deepcopy(overlay_object)
64
+
65
+ if start_object:
66
+ return start_object
67
+ if default:
68
+ return default() if callable(default) else copy.deepcopy(default)
69
+ return None
70
+
71
+ @staticmethod
72
+ def set_path_value(obj, path: str, value: object):
73
+ """
74
+ Given an object evaluate a path to set the value.
75
+ """
76
+
77
+ segments = ObjectPath.try_resolve_path(obj, path)
78
+ if not segments:
79
+ return
80
+
81
+ current = obj
82
+ for i in range(len(segments) - 1):
83
+ segment = segments[i]
84
+ if ObjectPath.is_int(segment):
85
+ index = int(segment)
86
+ next_obj = current[index]
87
+ if not next_obj and len(current) <= index:
88
+ # Expand list to index
89
+ current += [None] * ((index + 1) - len(current))
90
+ next_obj = current[index]
91
+ else:
92
+ next_obj = ObjectPath.__get_object_property(current, segment)
93
+ if not next_obj:
94
+ # Create object or list based on next segment
95
+ next_segment = segments[i + 1]
96
+ if not ObjectPath.is_int(next_segment):
97
+ ObjectPath.__set_object_segment(current, segment, {})
98
+ else:
99
+ ObjectPath.__set_object_segment(current, segment, [])
100
+
101
+ next_obj = ObjectPath.__get_object_property(current, segment)
102
+
103
+ current = next_obj
104
+
105
+ last_segment = segments[-1]
106
+ ObjectPath.__set_object_segment(current, last_segment, value)
107
+
108
+ @staticmethod
109
+ def get_path_value(
110
+ obj, path: str, default: Union[Callable, object] = None
111
+ ) -> object:
112
+ """
113
+ Get the value for a path relative to an object.
114
+ """
115
+
116
+ value = ObjectPath.try_get_path_value(obj, path)
117
+ if value:
118
+ return value
119
+
120
+ if default is None:
121
+ raise KeyError(f"Key {path} not found")
122
+ return default() if callable(default) else copy.deepcopy(default)
123
+
124
+ @staticmethod
125
+ def has_value(obj, path: str) -> bool:
126
+ """
127
+ Does an object have a subpath.
128
+ """
129
+ return ObjectPath.try_get_path_value(obj, path) is not None
130
+
131
+ @staticmethod
132
+ def remove_path_value(obj, path: str):
133
+ """
134
+ Remove path from object.
135
+ """
136
+
137
+ segments = ObjectPath.try_resolve_path(obj, path)
138
+ if not segments:
139
+ return
140
+
141
+ current = obj
142
+ for i in range(len(segments) - 1):
143
+ segment = segments[i]
144
+ current = ObjectPath.__resolve_segment(current, segment)
145
+ if not current:
146
+ return
147
+
148
+ if current:
149
+ last_segment = segments[-1]
150
+ if ObjectPath.is_int(last_segment):
151
+ current[int(last_segment)] = None # type: ignore[index]
152
+ else:
153
+ current.pop(last_segment) # type: ignore[union-attr]
154
+
155
+ @staticmethod
156
+ def try_get_path_value(obj, path: str) -> object:
157
+ """
158
+ Get the value for a path relative to an object.
159
+ """
160
+
161
+ if not obj:
162
+ return None
163
+
164
+ if path is None:
165
+ return None
166
+
167
+ if not path:
168
+ return obj
169
+
170
+ segments = ObjectPath.try_resolve_path(obj, path)
171
+ if not segments:
172
+ return None
173
+
174
+ result = ObjectPath.__resolve_segments(obj, segments)
175
+ if not result:
176
+ return None
177
+
178
+ return result
179
+
180
+ @staticmethod
181
+ def __set_object_segment(obj, segment, value):
182
+ val = ObjectPath.__get_normalized_value(value)
183
+
184
+ if ObjectPath.is_int(segment):
185
+ # the target is an list
186
+ index = int(segment)
187
+
188
+ # size the list if needed
189
+ obj += [None] * ((index + 1) - len(obj))
190
+
191
+ obj[index] = val
192
+ return
193
+
194
+ # the target is a dictionary
195
+ obj[segment] = val
196
+
197
+ @staticmethod
198
+ def __get_normalized_value(value):
199
+ return value
200
+
201
+ @staticmethod
202
+ def try_resolve_path(
203
+ obj, property_path: str, evaluate: bool = False
204
+ ) -> list | None:
205
+ so_far = []
206
+ first = property_path[0] if property_path else " "
207
+ if first in ("'", '"'):
208
+ if not property_path.endswith(first):
209
+ return None
210
+
211
+ so_far.append(property_path[1 : len(property_path) - 2])
212
+ elif ObjectPath.is_int(property_path):
213
+ so_far.append(int(property_path))
214
+ else:
215
+ start = 0
216
+ i = 0
217
+
218
+ def emit():
219
+ nonlocal start, i
220
+ segment = property_path[start:i]
221
+ if segment:
222
+ so_far.append(segment)
223
+ start = i + 1
224
+
225
+ while i < len(property_path):
226
+ char = property_path[i]
227
+ if char in (".", "["):
228
+ emit()
229
+
230
+ if char == "[":
231
+ nesting = 1
232
+ i += 1
233
+ while i < len(property_path):
234
+ char = property_path[i]
235
+ if char == "[":
236
+ nesting += 1
237
+ elif char == "]":
238
+ nesting -= 1
239
+ if nesting == 0:
240
+ break
241
+ i += 1
242
+
243
+ if nesting > 0:
244
+ return None
245
+
246
+ expr = property_path[start:i]
247
+ start = i + 1
248
+ indexer = ObjectPath.try_resolve_path(obj, expr, True)
249
+ if not indexer:
250
+ return None
251
+
252
+ result = indexer[0]
253
+ if ObjectPath.is_int(result):
254
+ so_far.append(int(result))
255
+ else:
256
+ so_far.append(result)
257
+
258
+ i += 1
259
+
260
+ emit()
261
+
262
+ if evaluate:
263
+ result = ObjectPath.__resolve_segments(obj, so_far)
264
+ if not result:
265
+ return None
266
+
267
+ so_far.clear()
268
+ so_far.append(result)
269
+
270
+ return so_far
271
+
272
+ @staticmethod
273
+ def for_each_property(obj: object, action: Callable[[str, object], None]):
274
+ if isinstance(obj, dict):
275
+ for key, value in obj.items():
276
+ action(key, value)
277
+ elif hasattr(obj, "__dict__"):
278
+ for key, value in vars(obj).items():
279
+ action(key, value)
280
+
281
+ @staticmethod
282
+ def __resolve_segments(current, segments: list) -> object:
283
+ result = current
284
+
285
+ for segment in segments:
286
+ result = ObjectPath.__resolve_segment(result, segment)
287
+ if not result:
288
+ return None
289
+
290
+ return result
291
+
292
+ @staticmethod
293
+ def __resolve_segment(current, segment) -> object:
294
+ if current:
295
+ if ObjectPath.is_int(segment):
296
+ current = current[int(segment)]
297
+ else:
298
+ current = ObjectPath.__get_object_property(current, segment)
299
+
300
+ return current
301
+
302
+ @staticmethod
303
+ def __get_object_property(obj, property_name: str):
304
+ # doing a case insensitive search
305
+ property_name_lower = property_name.lower()
306
+ matching = [obj[key] for key in obj if key.lower() == property_name_lower]
307
+ return matching[0] if matching else None
308
+
309
+ @staticmethod
310
+ def is_int(value: str) -> bool:
311
+ try:
312
+ int(value)
313
+ return True
314
+ except ValueError:
315
+ return False
@@ -0,0 +1,22 @@
1
+ # Copyright (c) Microsoft Corporation. All rights reserved.
2
+ # Licensed under the MIT License.
3
+
4
+ from typing import Any
5
+
6
+ from .persisted_state_keys import PersistedStateKeys
7
+
8
+
9
+ class PersistedState:
10
+ def __init__(
11
+ self, keys: PersistedStateKeys | None = None, data: dict[str, Any] | None = None
12
+ ):
13
+ if keys and data:
14
+ self.user_state: dict[str, Any] = (
15
+ data[keys.user_state] if keys.user_state in data else {}
16
+ )
17
+ self.conversation_state: dict[str, Any] = (
18
+ data[keys.conversation_state] if keys.conversation_state in data else {}
19
+ )
20
+ else:
21
+ self.user_state: dict[str, Any] = {}
22
+ self.conversation_state: dict[str, Any] = {}
@@ -0,0 +1,8 @@
1
+ # Copyright (c) Microsoft Corporation. All rights reserved.
2
+ # Licensed under the MIT License.
3
+
4
+
5
+ class PersistedStateKeys:
6
+ def __init__(self):
7
+ self.user_state: str | None = None
8
+ self.conversation_state: str | None = None
@@ -0,0 +1,41 @@
1
+ # coding=utf-8
2
+ # --------------------------------------------------------------------------
3
+ # Copyright (c) Microsoft Corporation. All rights reserved.
4
+ # Licensed under the MIT License. See License.txt in the project root for
5
+ # license information.
6
+ # --------------------------------------------------------------------------
7
+
8
+ from .activity_prompt import ActivityPrompt
9
+ from .attachment_prompt import AttachmentPrompt
10
+ from .choice_prompt import ChoicePrompt
11
+ from .confirm_prompt import ConfirmPrompt
12
+ from .datetime_prompt import DateTimePrompt
13
+ from .datetime_resolution import DateTimeResolution
14
+ from .number_prompt import NumberPrompt
15
+ from .oauth_prompt import OAuthPrompt
16
+ from .oauth_prompt_settings import OAuthPromptSettings
17
+ from .prompt_culture_models import PromptCultureModel, PromptCultureModels
18
+ from .prompt_options import PromptOptions
19
+ from .prompt_recognizer_result import PromptRecognizerResult
20
+ from .prompt_validator_context import PromptValidatorContext
21
+ from .prompt import Prompt
22
+ from .text_prompt import TextPrompt
23
+
24
+ __all__ = [
25
+ "ActivityPrompt",
26
+ "AttachmentPrompt",
27
+ "ChoicePrompt",
28
+ "ConfirmPrompt",
29
+ "DateTimePrompt",
30
+ "DateTimeResolution",
31
+ "NumberPrompt",
32
+ "OAuthPrompt",
33
+ "OAuthPromptSettings",
34
+ "PromptCultureModel",
35
+ "PromptCultureModels",
36
+ "PromptRecognizerResult",
37
+ "PromptValidatorContext",
38
+ "Prompt",
39
+ "PromptOptions",
40
+ "TextPrompt",
41
+ ]