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.
- microsoft_agents/hosting/dialogs/__init__.py +76 -0
- microsoft_agents/hosting/dialogs/_component_registration.py +30 -0
- microsoft_agents/hosting/dialogs/_telemetry_client.py +78 -0
- microsoft_agents/hosting/dialogs/choices/__init__.py +38 -0
- microsoft_agents/hosting/dialogs/choices/channel.py +121 -0
- microsoft_agents/hosting/dialogs/choices/choice_factory.py +262 -0
- microsoft_agents/hosting/dialogs/choices/choice_recognizer.py +148 -0
- microsoft_agents/hosting/dialogs/choices/find.py +242 -0
- microsoft_agents/hosting/dialogs/choices/models/__init__.py +23 -0
- microsoft_agents/hosting/dialogs/choices/models/choice.py +14 -0
- microsoft_agents/hosting/dialogs/choices/models/choice_factory_options.py +13 -0
- microsoft_agents/hosting/dialogs/choices/models/find_choices_options.py +28 -0
- microsoft_agents/hosting/dialogs/choices/models/find_values_options.py +31 -0
- microsoft_agents/hosting/dialogs/choices/models/found_choice.py +22 -0
- microsoft_agents/hosting/dialogs/choices/models/found_value.py +20 -0
- microsoft_agents/hosting/dialogs/choices/models/list_style.py +15 -0
- microsoft_agents/hosting/dialogs/choices/models/model_result.py +16 -0
- microsoft_agents/hosting/dialogs/choices/models/sorted_value.py +16 -0
- microsoft_agents/hosting/dialogs/choices/models/token.py +20 -0
- microsoft_agents/hosting/dialogs/choices/tokenizer.py +92 -0
- microsoft_agents/hosting/dialogs/component_dialog.py +284 -0
- microsoft_agents/hosting/dialogs/dialog.py +198 -0
- microsoft_agents/hosting/dialogs/dialog_component_registration.py +52 -0
- microsoft_agents/hosting/dialogs/dialog_container.py +31 -0
- microsoft_agents/hosting/dialogs/dialog_context.py +426 -0
- microsoft_agents/hosting/dialogs/dialog_extensions.py +201 -0
- microsoft_agents/hosting/dialogs/dialog_manager.py +189 -0
- microsoft_agents/hosting/dialogs/dialog_manager_result.py +17 -0
- microsoft_agents/hosting/dialogs/dialog_set.py +174 -0
- microsoft_agents/hosting/dialogs/dialog_state.py +20 -0
- microsoft_agents/hosting/dialogs/memory/__init__.py +24 -0
- microsoft_agents/hosting/dialogs/memory/component_memory_scopes_base.py +14 -0
- microsoft_agents/hosting/dialogs/memory/component_path_resolvers_base.py +15 -0
- microsoft_agents/hosting/dialogs/memory/dialog_path.py +33 -0
- microsoft_agents/hosting/dialogs/memory/dialog_state_manager.py +563 -0
- microsoft_agents/hosting/dialogs/memory/dialog_state_manager_configuration.py +11 -0
- microsoft_agents/hosting/dialogs/memory/path_resolver_base.py +8 -0
- microsoft_agents/hosting/dialogs/memory/path_resolvers/__init__.py +19 -0
- microsoft_agents/hosting/dialogs/memory/path_resolvers/alias_path_resolver.py +53 -0
- microsoft_agents/hosting/dialogs/memory/path_resolvers/at_at_path_resolver.py +9 -0
- microsoft_agents/hosting/dialogs/memory/path_resolvers/at_path_resolver.py +44 -0
- microsoft_agents/hosting/dialogs/memory/path_resolvers/dollar_path_resolver.py +9 -0
- microsoft_agents/hosting/dialogs/memory/path_resolvers/hash_path_resolver.py +9 -0
- microsoft_agents/hosting/dialogs/memory/path_resolvers/percent_path_resolver.py +9 -0
- microsoft_agents/hosting/dialogs/memory/scope_path.py +38 -0
- microsoft_agents/hosting/dialogs/memory/scopes/__init__.py +31 -0
- microsoft_agents/hosting/dialogs/memory/scopes/bot_state_memory_scope.py +66 -0
- microsoft_agents/hosting/dialogs/memory/scopes/class_memory_scope.py +64 -0
- microsoft_agents/hosting/dialogs/memory/scopes/conversation_memory_scope.py +12 -0
- microsoft_agents/hosting/dialogs/memory/scopes/dialog_class_memory_scope.py +52 -0
- microsoft_agents/hosting/dialogs/memory/scopes/dialog_context_memory_scope.py +68 -0
- microsoft_agents/hosting/dialogs/memory/scopes/dialog_memory_scope.py +75 -0
- microsoft_agents/hosting/dialogs/memory/scopes/memory_scope.py +91 -0
- microsoft_agents/hosting/dialogs/memory/scopes/settings_memory_scope.py +38 -0
- microsoft_agents/hosting/dialogs/memory/scopes/this_memory_scope.py +36 -0
- microsoft_agents/hosting/dialogs/memory/scopes/turn_memory_scope.py +86 -0
- microsoft_agents/hosting/dialogs/memory/scopes/user_memory_scope.py +12 -0
- microsoft_agents/hosting/dialogs/models/__init__.py +15 -0
- microsoft_agents/hosting/dialogs/models/dialog_event.py +13 -0
- microsoft_agents/hosting/dialogs/models/dialog_events.py +12 -0
- microsoft_agents/hosting/dialogs/models/dialog_instance.py +28 -0
- microsoft_agents/hosting/dialogs/models/dialog_reason.py +34 -0
- microsoft_agents/hosting/dialogs/models/dialog_turn_result.py +17 -0
- microsoft_agents/hosting/dialogs/models/dialog_turn_status.py +26 -0
- microsoft_agents/hosting/dialogs/object_path.py +315 -0
- microsoft_agents/hosting/dialogs/persisted_state.py +22 -0
- microsoft_agents/hosting/dialogs/persisted_state_keys.py +8 -0
- microsoft_agents/hosting/dialogs/prompts/__init__.py +41 -0
- microsoft_agents/hosting/dialogs/prompts/activity_prompt.py +203 -0
- microsoft_agents/hosting/dialogs/prompts/attachment_prompt.py +87 -0
- microsoft_agents/hosting/dialogs/prompts/choice_prompt.py +156 -0
- microsoft_agents/hosting/dialogs/prompts/confirm_prompt.py +161 -0
- microsoft_agents/hosting/dialogs/prompts/datetime_prompt.py +90 -0
- microsoft_agents/hosting/dialogs/prompts/datetime_resolution.py +16 -0
- microsoft_agents/hosting/dialogs/prompts/number_prompt.py +81 -0
- microsoft_agents/hosting/dialogs/prompts/oauth_prompt.py +569 -0
- microsoft_agents/hosting/dialogs/prompts/oauth_prompt_settings.py +43 -0
- microsoft_agents/hosting/dialogs/prompts/prompt.py +224 -0
- microsoft_agents/hosting/dialogs/prompts/prompt_culture_models.py +222 -0
- microsoft_agents/hosting/dialogs/prompts/prompt_options.py +42 -0
- microsoft_agents/hosting/dialogs/prompts/prompt_recognizer_result.py +11 -0
- microsoft_agents/hosting/dialogs/prompts/prompt_validator.py +0 -0
- microsoft_agents/hosting/dialogs/prompts/prompt_validator_context.py +44 -0
- microsoft_agents/hosting/dialogs/prompts/text_prompt.py +82 -0
- microsoft_agents/hosting/dialogs/waterfall_dialog.py +266 -0
- microsoft_agents/hosting/dialogs/waterfall_step_context.py +109 -0
- microsoft_agents_hosting_dialogs-0.10.0.dev2.dist-info/METADATA +87 -0
- microsoft_agents_hosting_dialogs-0.10.0.dev2.dist-info/RECORD +91 -0
- microsoft_agents_hosting_dialogs-0.10.0.dev2.dist-info/WHEEL +5 -0
- microsoft_agents_hosting_dialogs-0.10.0.dev2.dist-info/licenses/LICENSE +21 -0
- microsoft_agents_hosting_dialogs-0.10.0.dev2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
|
|
4
|
+
from .models.token import Token
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Tokenizer:
|
|
8
|
+
"""Provides a default tokenizer implementation."""
|
|
9
|
+
|
|
10
|
+
@staticmethod
|
|
11
|
+
def default_tokenizer( # pylint: disable=unused-argument
|
|
12
|
+
text: str, locale: str | None = None
|
|
13
|
+
) -> list[Token]:
|
|
14
|
+
"""
|
|
15
|
+
Simple tokenizer that breaks on spaces and punctuation. The only normalization is to lowercase.
|
|
16
|
+
|
|
17
|
+
Parameter:
|
|
18
|
+
---------
|
|
19
|
+
|
|
20
|
+
text: The input text.
|
|
21
|
+
|
|
22
|
+
locale: (Optional) Identifies the locale of the input text.
|
|
23
|
+
"""
|
|
24
|
+
tokens: list[Token] = []
|
|
25
|
+
token: Token | None = None
|
|
26
|
+
|
|
27
|
+
# Parse text
|
|
28
|
+
length: int = len(text) if text else 0
|
|
29
|
+
i: int = 0
|
|
30
|
+
|
|
31
|
+
while i < length:
|
|
32
|
+
# Get both the UNICODE value of the current character and the complete character itself
|
|
33
|
+
# which can potentially be multiple segments
|
|
34
|
+
code_point = ord(text[i])
|
|
35
|
+
char = chr(code_point)
|
|
36
|
+
|
|
37
|
+
# Process current character
|
|
38
|
+
if Tokenizer._is_breaking_char(code_point):
|
|
39
|
+
# Character is in Unicode Plane 0 and is in an excluded block
|
|
40
|
+
Tokenizer._append_token(tokens, token, i - 1)
|
|
41
|
+
token = None
|
|
42
|
+
elif code_point > 0xFFFF:
|
|
43
|
+
# Character is in a Supplementary Unicode Plane. This is where emoji live so
|
|
44
|
+
# we're going to just break each character in this range out as its own token
|
|
45
|
+
Tokenizer._append_token(tokens, token, i - 1)
|
|
46
|
+
token = None
|
|
47
|
+
tokens.append(Token(start=i, end=i, text=char, normalized=char))
|
|
48
|
+
elif token is None:
|
|
49
|
+
# Start a new token
|
|
50
|
+
token = Token(start=i, end=0, text=char, normalized=None)
|
|
51
|
+
else:
|
|
52
|
+
# Add onto current token
|
|
53
|
+
token.text += char
|
|
54
|
+
|
|
55
|
+
i += 1
|
|
56
|
+
|
|
57
|
+
Tokenizer._append_token(tokens, token, length - 1)
|
|
58
|
+
|
|
59
|
+
return tokens
|
|
60
|
+
|
|
61
|
+
@staticmethod
|
|
62
|
+
def _is_breaking_char(code_point) -> bool:
|
|
63
|
+
return (
|
|
64
|
+
Tokenizer._is_between(code_point, 0x0000, 0x002F)
|
|
65
|
+
or Tokenizer._is_between(code_point, 0x003A, 0x0040)
|
|
66
|
+
or Tokenizer._is_between(code_point, 0x005B, 0x0060)
|
|
67
|
+
or Tokenizer._is_between(code_point, 0x007B, 0x00BF)
|
|
68
|
+
or Tokenizer._is_between(code_point, 0x02B9, 0x036F)
|
|
69
|
+
or Tokenizer._is_between(code_point, 0x2000, 0x2BFF)
|
|
70
|
+
or Tokenizer._is_between(code_point, 0x2E00, 0x2E7F)
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
@staticmethod
|
|
74
|
+
def _is_between(value: int, from_val: int, to_val: int) -> bool:
|
|
75
|
+
"""
|
|
76
|
+
Parameters
|
|
77
|
+
-----------
|
|
78
|
+
|
|
79
|
+
value: number value
|
|
80
|
+
|
|
81
|
+
from: low range
|
|
82
|
+
|
|
83
|
+
to: high range
|
|
84
|
+
"""
|
|
85
|
+
return from_val <= value <= to_val
|
|
86
|
+
|
|
87
|
+
@staticmethod
|
|
88
|
+
def _append_token(tokens: list[Token], token: Token | None, end: int):
|
|
89
|
+
if token is not None:
|
|
90
|
+
token.end = end
|
|
91
|
+
token.normalized = token.text.lower()
|
|
92
|
+
tokens.append(token)
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from microsoft_agents.hosting.core import TurnContext
|
|
7
|
+
|
|
8
|
+
from .dialog import Dialog
|
|
9
|
+
from .dialog_context import DialogContext
|
|
10
|
+
from .models.dialog_turn_result import DialogTurnResult
|
|
11
|
+
from .dialog_state import DialogState
|
|
12
|
+
from .models.dialog_turn_status import DialogTurnStatus
|
|
13
|
+
from .models.dialog_reason import DialogReason
|
|
14
|
+
from .dialog_set import DialogSet
|
|
15
|
+
from .models.dialog_instance import DialogInstance
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ComponentDialog(Dialog):
|
|
19
|
+
"""
|
|
20
|
+
A :class:`microsoft_agents.hosting.dialogs.Dialog` that is composed of other dialogs
|
|
21
|
+
|
|
22
|
+
:var persisted_dialog state:
|
|
23
|
+
:vartype persisted_dialog_state: str
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
persisted_dialog_state = "dialogs"
|
|
27
|
+
|
|
28
|
+
def __init__(self, dialog_id: str):
|
|
29
|
+
"""
|
|
30
|
+
Initializes a new instance of the :class:`ComponentDialog`
|
|
31
|
+
|
|
32
|
+
:param dialog_id: The ID to assign to the new dialog within the parent dialog set.
|
|
33
|
+
:type dialog_id: str
|
|
34
|
+
"""
|
|
35
|
+
super(ComponentDialog, self).__init__(dialog_id)
|
|
36
|
+
|
|
37
|
+
if dialog_id is None:
|
|
38
|
+
raise TypeError("ComponentDialog(): dialog_id cannot be None.")
|
|
39
|
+
|
|
40
|
+
self._dialogs = DialogSet()
|
|
41
|
+
self.initial_dialog_id = None
|
|
42
|
+
|
|
43
|
+
# TODO: Add TelemetryClient
|
|
44
|
+
|
|
45
|
+
async def begin_dialog(
|
|
46
|
+
self, dialog_context: DialogContext, options: Any = None
|
|
47
|
+
) -> DialogTurnResult:
|
|
48
|
+
"""
|
|
49
|
+
Called when the dialog is started and pushed onto the parent's dialog stack.
|
|
50
|
+
|
|
51
|
+
If the task is successful, the result indicates whether the dialog is still
|
|
52
|
+
active after the turn has been processed by the dialog.
|
|
53
|
+
|
|
54
|
+
:param dialog_context: The :class:`botbuilder.dialogs.DialogContext` for the current turn of the conversation.
|
|
55
|
+
:type dialog_context: :class:`botbuilder.dialogs.DialogContext`
|
|
56
|
+
:param options: Optional, initial information to pass to the dialog.
|
|
57
|
+
:type options: Any
|
|
58
|
+
:return: Signals the end of the turn
|
|
59
|
+
:rtype: :class:`microsoft_agents.hosting.dialogs.Dialog.end_of_turn`
|
|
60
|
+
"""
|
|
61
|
+
if dialog_context is None:
|
|
62
|
+
raise TypeError("ComponentDialog.begin_dialog(): outer_dc cannot be None.")
|
|
63
|
+
|
|
64
|
+
# Start the inner dialog.
|
|
65
|
+
dialog_state = DialogState()
|
|
66
|
+
assert dialog_context.active_dialog is not None
|
|
67
|
+
dialog_context.active_dialog.state[self.persisted_dialog_state] = dialog_state
|
|
68
|
+
inner_dc = DialogContext(self._dialogs, dialog_context.context, dialog_state)
|
|
69
|
+
inner_dc.parent = dialog_context
|
|
70
|
+
turn_result = await self.on_begin_dialog(inner_dc, options)
|
|
71
|
+
|
|
72
|
+
# Check for end of inner dialog
|
|
73
|
+
if turn_result.status != DialogTurnStatus.Waiting:
|
|
74
|
+
# Return result to calling dialog
|
|
75
|
+
return await self.end_component(dialog_context, turn_result.result)
|
|
76
|
+
|
|
77
|
+
# Just signal waiting
|
|
78
|
+
return Dialog.end_of_turn
|
|
79
|
+
|
|
80
|
+
async def continue_dialog(self, dialog_context: DialogContext) -> DialogTurnResult:
|
|
81
|
+
"""
|
|
82
|
+
Called when the dialog is continued, where it is the active dialog and the
|
|
83
|
+
user replies with a new activity.
|
|
84
|
+
|
|
85
|
+
.. remarks::
|
|
86
|
+
If the task is successful, the result indicates whether the dialog is still
|
|
87
|
+
active after the turn has been processed by the dialog. The result may also
|
|
88
|
+
contain a return value.
|
|
89
|
+
|
|
90
|
+
If this method is *not* overriden the component dialog calls the
|
|
91
|
+
:meth:`microsoft_agents.hosting.dialogs.DialogContext.continue_dialog` method on it's inner dialog
|
|
92
|
+
context. If the inner dialog stack is empty, the component dialog ends,
|
|
93
|
+
and if a :class:`microsoft_agents.hosting.dialogs.DialogTurnResult.result` is available, the component dialog
|
|
94
|
+
uses that as it's return value.
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
:param dialog_context: The parent dialog context for the current turn of the conversation.
|
|
98
|
+
:type dialog_context: :class:`microsoft_agents.hosting.dialogs.DialogContext`
|
|
99
|
+
:return: Signals the end of the turn
|
|
100
|
+
:rtype: :class:`microsoft_agents.hosting.dialogs.Dialog.end_of_turn`
|
|
101
|
+
"""
|
|
102
|
+
if dialog_context is None:
|
|
103
|
+
raise TypeError("ComponentDialog.begin_dialog(): outer_dc cannot be None.")
|
|
104
|
+
|
|
105
|
+
# Continue execution of inner dialog.
|
|
106
|
+
assert dialog_context.active_dialog is not None
|
|
107
|
+
dialog_state = dialog_context.active_dialog.state[self.persisted_dialog_state]
|
|
108
|
+
inner_dc = DialogContext(self._dialogs, dialog_context.context, dialog_state)
|
|
109
|
+
inner_dc.parent = dialog_context
|
|
110
|
+
turn_result = await self.on_continue_dialog(inner_dc)
|
|
111
|
+
|
|
112
|
+
if turn_result.status != DialogTurnStatus.Waiting:
|
|
113
|
+
return await self.end_component(dialog_context, turn_result.result)
|
|
114
|
+
|
|
115
|
+
return Dialog.end_of_turn
|
|
116
|
+
|
|
117
|
+
async def resume_dialog(
|
|
118
|
+
self, dialog_context: DialogContext, reason: DialogReason, result: object = None
|
|
119
|
+
) -> DialogTurnResult:
|
|
120
|
+
"""
|
|
121
|
+
Called when a child dialog on the parent's dialog stack completed this turn, returning
|
|
122
|
+
control to this dialog component.
|
|
123
|
+
|
|
124
|
+
.. remarks::
|
|
125
|
+
Containers are typically leaf nodes on the stack but the dev is free to push other dialogs
|
|
126
|
+
on top of the stack which will result in the container receiving an unexpected call to
|
|
127
|
+
:meth:`ComponentDialog.resume_dialog()` when the pushed on dialog ends.
|
|
128
|
+
To avoid the container prematurely ending we need to implement this method and simply
|
|
129
|
+
ask our inner dialog stack to re-prompt.
|
|
130
|
+
|
|
131
|
+
:param dialog_context: The dialog context for the current turn of the conversation.
|
|
132
|
+
:type dialog_context: :class:`microsoft_agents.hosting.dialogs.DialogContext`
|
|
133
|
+
:param reason: Reason why the dialog resumed.
|
|
134
|
+
:type reason: :class:`microsoft_agents.hosting.dialogs.DialogReason`
|
|
135
|
+
:param result: Optional, value returned from the dialog that was called.
|
|
136
|
+
:type result: object
|
|
137
|
+
:return: Signals the end of the turn
|
|
138
|
+
:rtype: :class:`microsoft_agents.hosting.dialogs.Dialog.end_of_turn`
|
|
139
|
+
"""
|
|
140
|
+
|
|
141
|
+
assert dialog_context.active_dialog is not None
|
|
142
|
+
await self.reprompt_dialog(dialog_context.context, dialog_context.active_dialog)
|
|
143
|
+
return Dialog.end_of_turn
|
|
144
|
+
|
|
145
|
+
async def reprompt_dialog(
|
|
146
|
+
self, context: TurnContext, instance: DialogInstance
|
|
147
|
+
) -> None:
|
|
148
|
+
"""
|
|
149
|
+
Called when the dialog should re-prompt the user for input.
|
|
150
|
+
|
|
151
|
+
:param context: The context object for this turn.
|
|
152
|
+
:type context: :class:`microsoft_agents.hosting.dialogs.TurnContext`
|
|
153
|
+
:param instance: State information for this dialog.
|
|
154
|
+
:type instance: :class:`microsoft_agents.hosting.dialogs.DialogInstance`
|
|
155
|
+
"""
|
|
156
|
+
# Delegate to inner dialog.
|
|
157
|
+
dialog_state = instance.state[self.persisted_dialog_state]
|
|
158
|
+
inner_dc = DialogContext(self._dialogs, context, dialog_state)
|
|
159
|
+
await inner_dc.reprompt_dialog()
|
|
160
|
+
|
|
161
|
+
# Notify component
|
|
162
|
+
await self.on_reprompt_dialog(context, instance)
|
|
163
|
+
|
|
164
|
+
async def end_dialog(
|
|
165
|
+
self, context: TurnContext, instance: DialogInstance, reason: DialogReason
|
|
166
|
+
) -> None:
|
|
167
|
+
"""
|
|
168
|
+
Called when the dialog is ending.
|
|
169
|
+
|
|
170
|
+
:param context: The context object for this turn.
|
|
171
|
+
:type context: :class:`microsoft_agents.hosting.dialogs.TurnContext`
|
|
172
|
+
:param instance: State information associated with the instance of this component dialog.
|
|
173
|
+
:type instance: :class:`microsoft_agents.hosting.dialogs.DialogInstance`
|
|
174
|
+
:param reason: Reason why the dialog ended.
|
|
175
|
+
:type reason: :class:`microsoft_agents.hosting.dialogs.DialogReason`
|
|
176
|
+
"""
|
|
177
|
+
# Forward cancel to inner dialog
|
|
178
|
+
if reason == DialogReason.CancelCalled:
|
|
179
|
+
dialog_state = instance.state[self.persisted_dialog_state]
|
|
180
|
+
inner_dc = DialogContext(self._dialogs, context, dialog_state)
|
|
181
|
+
await inner_dc.cancel_all_dialogs()
|
|
182
|
+
await self.on_end_dialog(context, instance, reason)
|
|
183
|
+
|
|
184
|
+
def add_dialog(self, dialog: Dialog) -> object:
|
|
185
|
+
"""
|
|
186
|
+
Adds a :class:`Dialog` to the component dialog and returns the updated component.
|
|
187
|
+
|
|
188
|
+
:param dialog: The dialog to add.
|
|
189
|
+
:return: The updated :class:`ComponentDialog`.
|
|
190
|
+
:rtype: :class:`ComponentDialog`
|
|
191
|
+
"""
|
|
192
|
+
self._dialogs.add(dialog)
|
|
193
|
+
if not self.initial_dialog_id:
|
|
194
|
+
self.initial_dialog_id = dialog.id
|
|
195
|
+
return self
|
|
196
|
+
|
|
197
|
+
async def find_dialog(self, dialog_id: str | None) -> Dialog | None:
|
|
198
|
+
"""
|
|
199
|
+
Finds a dialog by ID.
|
|
200
|
+
|
|
201
|
+
:param dialog_id: The dialog to add.
|
|
202
|
+
:return: The dialog; or None if there is not a match for the ID.
|
|
203
|
+
:rtype: :class:`botbuilder.dialogs.Dialog`
|
|
204
|
+
"""
|
|
205
|
+
return await self._dialogs.find(dialog_id)
|
|
206
|
+
|
|
207
|
+
async def on_begin_dialog(
|
|
208
|
+
self, inner_dc: DialogContext, options: object
|
|
209
|
+
) -> DialogTurnResult:
|
|
210
|
+
"""
|
|
211
|
+
Called when the dialog is started and pushed onto the parent's dialog stack.
|
|
212
|
+
|
|
213
|
+
.. remarks::
|
|
214
|
+
If the task is successful, the result indicates whether the dialog is still
|
|
215
|
+
active after the turn has been processed by the dialog.
|
|
216
|
+
|
|
217
|
+
By default, this calls the :meth:`microsoft_agents.hosting.dialogs.Dialog.begin_dialog()`
|
|
218
|
+
method of the component dialog's initial dialog.
|
|
219
|
+
|
|
220
|
+
Override this method in a derived class to implement interrupt logic.
|
|
221
|
+
|
|
222
|
+
:param inner_dc: The inner dialog context for the current turn of conversation.
|
|
223
|
+
:type inner_dc: :class:`microsoft_agents.hosting.dialogs.DialogContext`
|
|
224
|
+
:param options: Optional, initial information to pass to the dialog.
|
|
225
|
+
:type options: object
|
|
226
|
+
"""
|
|
227
|
+
assert (
|
|
228
|
+
self.initial_dialog_id is not None
|
|
229
|
+
), "ComponentDialog: initial_dialog_id must be set before begin_dialog is called."
|
|
230
|
+
return await inner_dc.begin_dialog(self.initial_dialog_id, options)
|
|
231
|
+
|
|
232
|
+
async def on_continue_dialog(self, inner_dc: DialogContext) -> DialogTurnResult:
|
|
233
|
+
"""
|
|
234
|
+
Called when the dialog is continued, where it is the active dialog and the user replies with a new activity.
|
|
235
|
+
|
|
236
|
+
:param inner_dc: The inner dialog context for the current turn of conversation.
|
|
237
|
+
:type inner_dc: :class:`botbuilder.dialogs.DialogContext`
|
|
238
|
+
"""
|
|
239
|
+
return await inner_dc.continue_dialog()
|
|
240
|
+
|
|
241
|
+
async def on_end_dialog( # pylint: disable=unused-argument
|
|
242
|
+
self, context: TurnContext, instance: DialogInstance, reason: DialogReason
|
|
243
|
+
) -> None:
|
|
244
|
+
"""
|
|
245
|
+
Ends the component dialog in its parent's context.
|
|
246
|
+
|
|
247
|
+
:param turn_context: The :class:`botbuilder.core.TurnContext` for the current turn of the conversation.
|
|
248
|
+
:type turn_context: :class:`botbuilder.core.TurnContext`
|
|
249
|
+
:param instance: State information associated with the inner dialog stack of this component dialog.
|
|
250
|
+
:type instance: :class:`botbuilder.dialogs.DialogInstance`
|
|
251
|
+
:param reason: Reason why the dialog ended.
|
|
252
|
+
:type reason: :class:`botbuilder.dialogs.DialogReason`
|
|
253
|
+
"""
|
|
254
|
+
return
|
|
255
|
+
|
|
256
|
+
async def on_reprompt_dialog( # pylint: disable=unused-argument
|
|
257
|
+
self, turn_context: TurnContext, instance: DialogInstance
|
|
258
|
+
) -> None:
|
|
259
|
+
"""
|
|
260
|
+
:param turn_context: The :class:`botbuilder.core.TurnContext` for the current turn of the conversation.
|
|
261
|
+
:type turn_context: :class:`botbuilder.dialogs.DialogInstance`
|
|
262
|
+
:param instance: State information associated with the inner dialog stack of this component dialog.
|
|
263
|
+
:type instance: :class:`botbuilder.dialogs.DialogInstance`
|
|
264
|
+
"""
|
|
265
|
+
return
|
|
266
|
+
|
|
267
|
+
async def end_component(
|
|
268
|
+
self, outer_dc: DialogContext, result: object # pylint: disable=unused-argument
|
|
269
|
+
) -> DialogTurnResult:
|
|
270
|
+
"""
|
|
271
|
+
Ends the component dialog in its parent's context.
|
|
272
|
+
|
|
273
|
+
.. remarks::
|
|
274
|
+
If the task is successful, the result indicates that the dialog ended after the
|
|
275
|
+
turn was processed by the dialog.
|
|
276
|
+
|
|
277
|
+
:param outer_dc: The parent dialog context for the current turn of conversation.
|
|
278
|
+
:type outer_dc: class:`botbuilder.dialogs.DialogContext`
|
|
279
|
+
:param result: Optional, value to return from the dialog component to the parent context.
|
|
280
|
+
:type result: object
|
|
281
|
+
:return: Value to return.
|
|
282
|
+
:rtype: :class:`botbuilder.dialogs.DialogTurnResult.result`
|
|
283
|
+
"""
|
|
284
|
+
return await outer_dc.end_dialog(result)
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from abc import ABC, abstractmethod
|
|
7
|
+
from typing import TYPE_CHECKING
|
|
8
|
+
|
|
9
|
+
from microsoft_agents.hosting.core import TurnContext
|
|
10
|
+
|
|
11
|
+
from ._telemetry_client import AgentTelemetryClient, NullTelemetryClient
|
|
12
|
+
from .models.dialog_reason import DialogReason
|
|
13
|
+
from .models.dialog_event import DialogEvent
|
|
14
|
+
from .models.dialog_turn_status import DialogTurnStatus
|
|
15
|
+
from .models.dialog_turn_result import DialogTurnResult
|
|
16
|
+
from .models.dialog_instance import DialogInstance
|
|
17
|
+
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from .dialog_context import DialogContext
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class Dialog(ABC):
|
|
23
|
+
|
|
24
|
+
def __init__(self, dialog_id: str):
|
|
25
|
+
if dialog_id is None or not dialog_id.strip():
|
|
26
|
+
raise TypeError("Dialog(): dialogId cannot be None.")
|
|
27
|
+
|
|
28
|
+
self._telemetry_client = NullTelemetryClient()
|
|
29
|
+
self._id = dialog_id
|
|
30
|
+
|
|
31
|
+
end_of_turn = DialogTurnResult(DialogTurnStatus.Waiting)
|
|
32
|
+
"""DialogTurnResult indicating the dialog is waiting for new activity."""
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def id(self) -> str: # pylint: disable=invalid-name
|
|
36
|
+
return self._id
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def telemetry_client(self) -> AgentTelemetryClient:
|
|
40
|
+
"""
|
|
41
|
+
Gets the telemetry client for logging events.
|
|
42
|
+
"""
|
|
43
|
+
return self._telemetry_client
|
|
44
|
+
|
|
45
|
+
@telemetry_client.setter
|
|
46
|
+
def telemetry_client(self, value: AgentTelemetryClient) -> None:
|
|
47
|
+
"""
|
|
48
|
+
Sets the telemetry client for logging events.
|
|
49
|
+
"""
|
|
50
|
+
if value is None:
|
|
51
|
+
self._telemetry_client = NullTelemetryClient()
|
|
52
|
+
else:
|
|
53
|
+
self._telemetry_client = value
|
|
54
|
+
|
|
55
|
+
@abstractmethod
|
|
56
|
+
async def begin_dialog(
|
|
57
|
+
self, dialog_context: "DialogContext", options: object = None
|
|
58
|
+
):
|
|
59
|
+
"""
|
|
60
|
+
Method called when a new dialog has been pushed onto the stack and is being activated.
|
|
61
|
+
:param dialog_context: The dialog context for the current turn of conversation.
|
|
62
|
+
:param options: (Optional) additional argument(s) to pass to the dialog being started.
|
|
63
|
+
"""
|
|
64
|
+
raise NotImplementedError()
|
|
65
|
+
|
|
66
|
+
async def continue_dialog(self, dialog_context: "DialogContext"):
|
|
67
|
+
"""
|
|
68
|
+
Method called when an instance of the dialog is the "current" dialog and the
|
|
69
|
+
user replies with a new activity. The dialog will generally continue to receive the user's
|
|
70
|
+
replies until it calls either `end_dialog()` or `begin_dialog()`.
|
|
71
|
+
If this method is NOT implemented then the dialog will automatically be ended when the user replies.
|
|
72
|
+
:param dialog_context: The dialog context for the current turn of conversation.
|
|
73
|
+
:return:
|
|
74
|
+
"""
|
|
75
|
+
# By default just end the current dialog.
|
|
76
|
+
return await dialog_context.end_dialog(None)
|
|
77
|
+
|
|
78
|
+
async def resume_dialog( # pylint: disable=unused-argument
|
|
79
|
+
self, dialog_context: "DialogContext", reason: DialogReason, result: object
|
|
80
|
+
):
|
|
81
|
+
"""
|
|
82
|
+
Method called when an instance of the dialog is being returned to from another
|
|
83
|
+
dialog that was started by the current instance using `begin_dialog()`.
|
|
84
|
+
If this method is NOT implemented then the dialog will be automatically ended with a call
|
|
85
|
+
to `end_dialog()`. Any result passed from the called dialog will be passed
|
|
86
|
+
to the current dialog's parent. If there are no more parent dialogs on the stack then
|
|
87
|
+
processing of the turn will end.
|
|
88
|
+
:param dialog_context: The dialog context for the current turn of conversation.
|
|
89
|
+
:param reason: Reason why the dialog resumed.
|
|
90
|
+
:param result: (Optional) value returned from the dialog that was called.
|
|
91
|
+
:return:
|
|
92
|
+
"""
|
|
93
|
+
# By default just end the current dialog and return result to parent.
|
|
94
|
+
return await dialog_context.end_dialog(result)
|
|
95
|
+
|
|
96
|
+
async def reprompt_dialog( # pylint: disable=unused-argument
|
|
97
|
+
self, context: TurnContext, instance: DialogInstance
|
|
98
|
+
):
|
|
99
|
+
"""Called when the dialog should re-prompt the user for input.
|
|
100
|
+
|
|
101
|
+
Override this method to send a repeat of the most recent prompt activity.
|
|
102
|
+
The default implementation is a no-op.
|
|
103
|
+
|
|
104
|
+
:param context: The context for the current turn.
|
|
105
|
+
:param instance: The dialog instance on the stack.
|
|
106
|
+
"""
|
|
107
|
+
# No-op by default
|
|
108
|
+
return
|
|
109
|
+
|
|
110
|
+
async def end_dialog( # pylint: disable=unused-argument
|
|
111
|
+
self, context: TurnContext, instance: DialogInstance, reason: DialogReason
|
|
112
|
+
):
|
|
113
|
+
"""Called when the dialog is ending. Override to perform cleanup or send an
|
|
114
|
+
EndOfConversation activity.
|
|
115
|
+
|
|
116
|
+
The default implementation is a no-op; subclasses should call ``super()``
|
|
117
|
+
only if they need the no-op behaviour to remain in derived chains.
|
|
118
|
+
|
|
119
|
+
:param context: The context for the current turn.
|
|
120
|
+
:param instance: The dialog instance being ended.
|
|
121
|
+
:param reason: Why the dialog is ending (EndCalled, CancelCalled, or ReplaceCalled).
|
|
122
|
+
"""
|
|
123
|
+
# No-op by default
|
|
124
|
+
return
|
|
125
|
+
|
|
126
|
+
def get_version(self) -> str:
|
|
127
|
+
"""Gets a string that uniquely describes this dialog's version. Changing the version
|
|
128
|
+
indicates that a stored instance of the dialog may be incompatible with the current
|
|
129
|
+
definition. By default this returns the dialog's ID.
|
|
130
|
+
|
|
131
|
+
:return: Version string for this dialog.
|
|
132
|
+
"""
|
|
133
|
+
return self.id
|
|
134
|
+
|
|
135
|
+
async def on_dialog_event(
|
|
136
|
+
self, dialog_context: "DialogContext", dialog_event: DialogEvent
|
|
137
|
+
) -> bool:
|
|
138
|
+
"""
|
|
139
|
+
Called when an event has been raised, using `DialogContext.emitEvent()`, by either the current dialog or a
|
|
140
|
+
dialog that the current dialog started.
|
|
141
|
+
:param dialog_context: The dialog context for the current turn of conversation.
|
|
142
|
+
:param dialog_event: The event being raised.
|
|
143
|
+
:return: True if the event is handled by the current dialog and bubbling should stop.
|
|
144
|
+
"""
|
|
145
|
+
# Before bubble
|
|
146
|
+
handled = await self._on_pre_bubble_event(dialog_context, dialog_event)
|
|
147
|
+
|
|
148
|
+
# Bubble as needed
|
|
149
|
+
if (not handled) and dialog_event.bubble and dialog_context.parent:
|
|
150
|
+
handled = await dialog_context.parent.emit_event(
|
|
151
|
+
dialog_event.name, dialog_event.value, True, False
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
# Post bubble
|
|
155
|
+
if not handled:
|
|
156
|
+
handled = await self._on_post_bubble_event(dialog_context, dialog_event)
|
|
157
|
+
|
|
158
|
+
return handled
|
|
159
|
+
|
|
160
|
+
async def _on_pre_bubble_event( # pylint: disable=unused-argument
|
|
161
|
+
self, dialog_context: "DialogContext", dialog_event: DialogEvent
|
|
162
|
+
) -> bool:
|
|
163
|
+
"""
|
|
164
|
+
Called before an event is bubbled to its parent.
|
|
165
|
+
:param dialog_context: The dialog context for the current turn of conversation.
|
|
166
|
+
:param dialog_event: The event being raised.
|
|
167
|
+
:return: Whether the event is handled by the current dialog and further processing should stop.
|
|
168
|
+
"""
|
|
169
|
+
return False
|
|
170
|
+
|
|
171
|
+
async def _on_post_bubble_event( # pylint: disable=unused-argument
|
|
172
|
+
self, dialog_context: "DialogContext", dialog_event: DialogEvent
|
|
173
|
+
) -> bool:
|
|
174
|
+
"""
|
|
175
|
+
Called after an event was bubbled to all parents and wasn't handled.
|
|
176
|
+
:param dialog_context: The dialog context for the current turn of conversation.
|
|
177
|
+
:param dialog_event: The event being raised.
|
|
178
|
+
:return: Whether the event is handled by the current dialog and further processing should stop.
|
|
179
|
+
"""
|
|
180
|
+
return False
|
|
181
|
+
|
|
182
|
+
def _on_compute_id(self) -> str:
|
|
183
|
+
"""
|
|
184
|
+
Computes an unique ID for a dialog.
|
|
185
|
+
:return: An unique ID for a dialog
|
|
186
|
+
"""
|
|
187
|
+
return self.__class__.__name__
|
|
188
|
+
|
|
189
|
+
def _register_source_location(
|
|
190
|
+
self, path: str, line_number: int
|
|
191
|
+
): # pylint: disable=unused-argument
|
|
192
|
+
"""
|
|
193
|
+
Registers a SourceRange in the provided location.
|
|
194
|
+
:param path: The path to the source file.
|
|
195
|
+
:param line_number: The line number where the source will be located on the file.
|
|
196
|
+
"""
|
|
197
|
+
if path:
|
|
198
|
+
pass
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
|
|
4
|
+
from typing import Iterable
|
|
5
|
+
|
|
6
|
+
from microsoft_agents.hosting.dialogs.memory import (
|
|
7
|
+
ComponentMemoryScopesBase,
|
|
8
|
+
ComponentPathResolversBase,
|
|
9
|
+
PathResolverBase,
|
|
10
|
+
)
|
|
11
|
+
from microsoft_agents.hosting.dialogs.memory.scopes import (
|
|
12
|
+
TurnMemoryScope,
|
|
13
|
+
SettingsMemoryScope,
|
|
14
|
+
DialogMemoryScope,
|
|
15
|
+
DialogContextMemoryScope,
|
|
16
|
+
DialogClassMemoryScope,
|
|
17
|
+
ClassMemoryScope,
|
|
18
|
+
MemoryScope,
|
|
19
|
+
ThisMemoryScope,
|
|
20
|
+
ConversationMemoryScope,
|
|
21
|
+
UserMemoryScope,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
from microsoft_agents.hosting.dialogs.memory.path_resolvers import (
|
|
25
|
+
AtAtPathResolver,
|
|
26
|
+
AtPathResolver,
|
|
27
|
+
DollarPathResolver,
|
|
28
|
+
HashPathResolver,
|
|
29
|
+
PercentPathResolver,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class DialogsComponentRegistration(
|
|
34
|
+
ComponentMemoryScopesBase, ComponentPathResolversBase
|
|
35
|
+
):
|
|
36
|
+
def get_memory_scopes(self) -> Iterable[MemoryScope]:
|
|
37
|
+
yield TurnMemoryScope()
|
|
38
|
+
yield SettingsMemoryScope()
|
|
39
|
+
yield DialogMemoryScope()
|
|
40
|
+
yield DialogContextMemoryScope()
|
|
41
|
+
yield DialogClassMemoryScope()
|
|
42
|
+
yield ClassMemoryScope()
|
|
43
|
+
yield ThisMemoryScope()
|
|
44
|
+
yield ConversationMemoryScope()
|
|
45
|
+
yield UserMemoryScope()
|
|
46
|
+
|
|
47
|
+
def get_path_resolvers(self) -> Iterable[PathResolverBase]:
|
|
48
|
+
yield AtAtPathResolver()
|
|
49
|
+
yield AtPathResolver()
|
|
50
|
+
yield DollarPathResolver()
|
|
51
|
+
yield HashPathResolver()
|
|
52
|
+
yield PercentPathResolver()
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from abc import ABC, abstractmethod
|
|
7
|
+
from typing import TYPE_CHECKING
|
|
8
|
+
|
|
9
|
+
from .dialog import Dialog
|
|
10
|
+
from .dialog_set import DialogSet
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from .dialog_context import DialogContext
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class DialogContainer(Dialog, ABC):
|
|
17
|
+
|
|
18
|
+
def __init__(self, dialog_id: str):
|
|
19
|
+
super().__init__(dialog_id)
|
|
20
|
+
self.dialogs = DialogSet()
|
|
21
|
+
|
|
22
|
+
@abstractmethod
|
|
23
|
+
def create_child_context(self, dialog_context: DialogContext) -> DialogContext:
|
|
24
|
+
"""
|
|
25
|
+
Creates the inner dialog context for the active child dialog, if there is one.
|
|
26
|
+
:param dialog_context: The parent dialog context.
|
|
27
|
+
:return: The child dialog context, or None if there is no active child.
|
|
28
|
+
"""
|
|
29
|
+
raise NotImplementedError(
|
|
30
|
+
"DialogContainer.create_child_context(): not implemented."
|
|
31
|
+
)
|