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,189 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
|
|
4
|
+
from datetime import datetime, timedelta
|
|
5
|
+
from threading import Lock
|
|
6
|
+
from typing import cast
|
|
7
|
+
|
|
8
|
+
from microsoft_agents.hosting.core import (
|
|
9
|
+
ChannelAdapter,
|
|
10
|
+
ConversationState,
|
|
11
|
+
UserState,
|
|
12
|
+
TurnContext,
|
|
13
|
+
ClaimsIdentity,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
from .dialog import Dialog
|
|
17
|
+
from .dialog_context import DialogContext
|
|
18
|
+
from .dialog_extensions import DialogExtensions
|
|
19
|
+
from .dialog_set import DialogSet
|
|
20
|
+
from .dialog_state import DialogState
|
|
21
|
+
from .dialog_manager_result import DialogManagerResult
|
|
22
|
+
from .models.dialog_turn_status import DialogTurnStatus
|
|
23
|
+
from .models.dialog_turn_result import DialogTurnResult
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class DialogManager:
|
|
27
|
+
"""
|
|
28
|
+
Class which runs the dialog system.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def __init__(
|
|
32
|
+
self,
|
|
33
|
+
root_dialog: Dialog | None = None,
|
|
34
|
+
dialog_state_property: str = "DialogState",
|
|
35
|
+
):
|
|
36
|
+
"""
|
|
37
|
+
Initializes an instance of the DialogManager class.
|
|
38
|
+
:param root_dialog: Root dialog to use.
|
|
39
|
+
:param dialog_state_property: Alternate name for the dialog_state property. (Default is "DialogState").
|
|
40
|
+
"""
|
|
41
|
+
self.last_access = "_lastAccess"
|
|
42
|
+
self._root_dialog_id = ""
|
|
43
|
+
self._dialog_state_property = dialog_state_property
|
|
44
|
+
self._lock = Lock()
|
|
45
|
+
|
|
46
|
+
# Gets or sets root dialog to use to start conversation.
|
|
47
|
+
self.root_dialog = root_dialog
|
|
48
|
+
|
|
49
|
+
# Gets or sets the ConversationState.
|
|
50
|
+
self.conversation_state: ConversationState | None = None
|
|
51
|
+
|
|
52
|
+
# Gets or sets the UserState.
|
|
53
|
+
self.user_state: UserState | None = None
|
|
54
|
+
|
|
55
|
+
# Gets InitialTurnState collection to copy into the TurnState on every turn.
|
|
56
|
+
self.initial_turn_state = {}
|
|
57
|
+
|
|
58
|
+
# Gets or sets global dialogs that you want to have be callable.
|
|
59
|
+
self.dialogs = DialogSet()
|
|
60
|
+
|
|
61
|
+
# Gets or sets (optional) number of milliseconds to expire the bot's state after.
|
|
62
|
+
self.expire_after: int | None = None
|
|
63
|
+
|
|
64
|
+
async def on_turn(self, context: TurnContext) -> DialogManagerResult:
|
|
65
|
+
"""
|
|
66
|
+
Runs dialog system in the context of a TurnContext.
|
|
67
|
+
:param context: turn context.
|
|
68
|
+
:return:
|
|
69
|
+
"""
|
|
70
|
+
# Lazy initialize RootDialog so it can refer to assets like LG function templates
|
|
71
|
+
if not self._root_dialog_id:
|
|
72
|
+
with self._lock:
|
|
73
|
+
if not self._root_dialog_id:
|
|
74
|
+
if self.root_dialog is None:
|
|
75
|
+
raise ValueError("DialogManager: root_dialog cannot be None.")
|
|
76
|
+
self._root_dialog_id = self.root_dialog.id
|
|
77
|
+
self.dialogs.add(self.root_dialog)
|
|
78
|
+
|
|
79
|
+
# Preload TurnState with DM TurnState.
|
|
80
|
+
for key, val in self.initial_turn_state.items():
|
|
81
|
+
context.turn_state[key] = val
|
|
82
|
+
|
|
83
|
+
# Register DialogManager with TurnState.
|
|
84
|
+
context.turn_state[DialogManager.__name__] = self
|
|
85
|
+
|
|
86
|
+
# Resolve ConversationState
|
|
87
|
+
conversation_state_name = ConversationState.__name__
|
|
88
|
+
if self.conversation_state is None:
|
|
89
|
+
if conversation_state_name not in context.turn_state:
|
|
90
|
+
raise Exception(
|
|
91
|
+
f"Unable to get an instance of {conversation_state_name} from turn_context. "
|
|
92
|
+
f"Please ensure ConversationState is available in turn_state."
|
|
93
|
+
)
|
|
94
|
+
self.conversation_state = cast(
|
|
95
|
+
ConversationState, context.turn_state[conversation_state_name]
|
|
96
|
+
)
|
|
97
|
+
else:
|
|
98
|
+
context.turn_state[conversation_state_name] = self.conversation_state
|
|
99
|
+
|
|
100
|
+
# Resolve UserState (optional)
|
|
101
|
+
user_state_name = UserState.__name__
|
|
102
|
+
if self.user_state is None:
|
|
103
|
+
self.user_state = cast(
|
|
104
|
+
UserState | None, context.turn_state.get(user_state_name, None)
|
|
105
|
+
)
|
|
106
|
+
else:
|
|
107
|
+
context.turn_state[user_state_name] = self.user_state
|
|
108
|
+
|
|
109
|
+
# Create property accessors
|
|
110
|
+
last_access_property = self.conversation_state.create_property(self.last_access)
|
|
111
|
+
last_access: datetime = cast(
|
|
112
|
+
datetime, await last_access_property.get(context, datetime.now)
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
# Check for expired conversation
|
|
116
|
+
if self.expire_after is not None and (
|
|
117
|
+
datetime.now() - last_access
|
|
118
|
+
) >= timedelta(milliseconds=float(self.expire_after)):
|
|
119
|
+
# Clear conversation state
|
|
120
|
+
self.conversation_state.clear(context)
|
|
121
|
+
|
|
122
|
+
last_access = datetime.now()
|
|
123
|
+
await last_access_property.set(context, last_access)
|
|
124
|
+
|
|
125
|
+
# Get dialog stack
|
|
126
|
+
dialogs_property = self.conversation_state.create_property(
|
|
127
|
+
self._dialog_state_property
|
|
128
|
+
)
|
|
129
|
+
dialog_state: DialogState = cast(
|
|
130
|
+
DialogState, await dialogs_property.get(context, DialogState)
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
# Create DialogContext
|
|
134
|
+
dialog_context = DialogContext(self.dialogs, context, dialog_state)
|
|
135
|
+
|
|
136
|
+
# Call the common dialog "continue/begin" execution pattern shared with the classic RunAsync extension method
|
|
137
|
+
turn_result = (
|
|
138
|
+
await DialogExtensions._internal_run( # pylint: disable=protected-access
|
|
139
|
+
context, self._root_dialog_id, dialog_context
|
|
140
|
+
)
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
# Save ConversationState changes
|
|
144
|
+
await self.conversation_state.save(context, False)
|
|
145
|
+
|
|
146
|
+
# Save UserState changes if present
|
|
147
|
+
if self.user_state is not None:
|
|
148
|
+
await self.user_state.save(context, False)
|
|
149
|
+
|
|
150
|
+
return DialogManagerResult(turn_result=turn_result)
|
|
151
|
+
|
|
152
|
+
@staticmethod
|
|
153
|
+
def is_from_parent_to_skill(turn_context: TurnContext) -> bool:
|
|
154
|
+
"""
|
|
155
|
+
Determines if this turn is a request from a parent bot to this skill.
|
|
156
|
+
"""
|
|
157
|
+
|
|
158
|
+
claims_identity = turn_context.turn_state.get(
|
|
159
|
+
ChannelAdapter.AGENT_IDENTITY_KEY, None
|
|
160
|
+
)
|
|
161
|
+
return (
|
|
162
|
+
isinstance(claims_identity, ClaimsIdentity)
|
|
163
|
+
and claims_identity.is_agent_claim()
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
@staticmethod
|
|
167
|
+
def should_send_end_of_conversation_to_parent(
|
|
168
|
+
context: TurnContext, turn_result: DialogTurnResult
|
|
169
|
+
) -> bool:
|
|
170
|
+
"""
|
|
171
|
+
Helper to determine if we should send an EndOfConversation to the parent or not.
|
|
172
|
+
"""
|
|
173
|
+
if not (
|
|
174
|
+
turn_result.status == DialogTurnStatus.Complete
|
|
175
|
+
or turn_result.status == DialogTurnStatus.Cancelled
|
|
176
|
+
):
|
|
177
|
+
# The dialog is still going, don't return EoC.
|
|
178
|
+
return False
|
|
179
|
+
|
|
180
|
+
return DialogManager.is_from_parent_to_skill(context)
|
|
181
|
+
|
|
182
|
+
@staticmethod
|
|
183
|
+
def get_active_dialog_context(dialog_context: DialogContext) -> DialogContext:
|
|
184
|
+
"""
|
|
185
|
+
Recursively walk up the DC stack to find the active DC.
|
|
186
|
+
"""
|
|
187
|
+
return DialogExtensions._get_active_dialog_context( # pylint: disable=protected-access
|
|
188
|
+
dialog_context
|
|
189
|
+
)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
|
|
6
|
+
from microsoft_agents.activity import Activity
|
|
7
|
+
|
|
8
|
+
from .models.dialog_turn_result import DialogTurnResult
|
|
9
|
+
from .persisted_state import PersistedState
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class DialogManagerResult:
|
|
14
|
+
|
|
15
|
+
turn_result: DialogTurnResult | None = None
|
|
16
|
+
activities: list[Activity] = field(default_factory=list)
|
|
17
|
+
persisted_state: PersistedState | None = None
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import inspect
|
|
7
|
+
from hashlib import sha256
|
|
8
|
+
from typing import TYPE_CHECKING
|
|
9
|
+
|
|
10
|
+
from microsoft_agents.hosting.core import TurnContext, StatePropertyAccessor
|
|
11
|
+
|
|
12
|
+
from ._telemetry_client import AgentTelemetryClient, NullTelemetryClient
|
|
13
|
+
from .dialog import Dialog
|
|
14
|
+
from .dialog_state import DialogState
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from .dialog_context import DialogContext
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class DialogSet:
|
|
21
|
+
def __init__(self, dialog_state: StatePropertyAccessor | None = None):
|
|
22
|
+
# pylint: disable=import-outside-toplevel
|
|
23
|
+
if dialog_state is None:
|
|
24
|
+
current_frame = inspect.currentframe()
|
|
25
|
+
frame = current_frame.f_back if current_frame is not None else None
|
|
26
|
+
try:
|
|
27
|
+
# try to access the caller's "self"
|
|
28
|
+
try:
|
|
29
|
+
self_obj = frame.f_locals["self"] if frame is not None else None
|
|
30
|
+
if self_obj is None:
|
|
31
|
+
raise KeyError
|
|
32
|
+
except KeyError:
|
|
33
|
+
raise TypeError("DialogSet(): dialog_state cannot be None.")
|
|
34
|
+
# Only ComponentDialog / DialogContainer / DialogManager can initialize with None dialog_state
|
|
35
|
+
from .component_dialog import ComponentDialog
|
|
36
|
+
from .dialog_manager import DialogManager
|
|
37
|
+
from .dialog_container import DialogContainer
|
|
38
|
+
|
|
39
|
+
if not isinstance(
|
|
40
|
+
self_obj, (ComponentDialog, DialogContainer, DialogManager)
|
|
41
|
+
):
|
|
42
|
+
raise TypeError("DialogSet(): dialog_state cannot be None.")
|
|
43
|
+
finally:
|
|
44
|
+
# make sure to clean up the frame at the end to avoid ref cycles
|
|
45
|
+
del frame
|
|
46
|
+
|
|
47
|
+
self._dialog_state = dialog_state
|
|
48
|
+
self.__telemetry_client = NullTelemetryClient()
|
|
49
|
+
|
|
50
|
+
self._dialogs: dict[str, Dialog] = {}
|
|
51
|
+
self._version: str | None = None
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def telemetry_client(self) -> AgentTelemetryClient:
|
|
55
|
+
"""
|
|
56
|
+
Gets the telemetry client for logging events.
|
|
57
|
+
"""
|
|
58
|
+
return self.__telemetry_client
|
|
59
|
+
|
|
60
|
+
@telemetry_client.setter
|
|
61
|
+
def telemetry_client(self, value: AgentTelemetryClient) -> None:
|
|
62
|
+
"""
|
|
63
|
+
Sets the telemetry client for all dialogs in this set.
|
|
64
|
+
"""
|
|
65
|
+
if value is None:
|
|
66
|
+
self.__telemetry_client = NullTelemetryClient()
|
|
67
|
+
else:
|
|
68
|
+
self.__telemetry_client = value
|
|
69
|
+
|
|
70
|
+
for dialog in self._dialogs.values():
|
|
71
|
+
dialog.telemetry_client = self.__telemetry_client
|
|
72
|
+
self._version = None
|
|
73
|
+
|
|
74
|
+
def get_version(self) -> str:
|
|
75
|
+
"""
|
|
76
|
+
Gets a unique string which represents the combined versions of all dialogs in this dialogset.
|
|
77
|
+
Version will change when any of the child dialogs version changes.
|
|
78
|
+
"""
|
|
79
|
+
if not self._version:
|
|
80
|
+
version = ""
|
|
81
|
+
for _, dialog in self._dialogs.items():
|
|
82
|
+
aux_version = dialog.get_version()
|
|
83
|
+
if aux_version:
|
|
84
|
+
version += aux_version
|
|
85
|
+
|
|
86
|
+
self._version = sha256(version.encode()).hexdigest()
|
|
87
|
+
|
|
88
|
+
return self._version
|
|
89
|
+
|
|
90
|
+
def add(self, dialog: Dialog):
|
|
91
|
+
"""
|
|
92
|
+
Adds a new dialog to the set and returns the added dialog.
|
|
93
|
+
:param dialog: The dialog to add.
|
|
94
|
+
"""
|
|
95
|
+
if dialog is None or not isinstance(dialog, Dialog):
|
|
96
|
+
raise TypeError(
|
|
97
|
+
"DialogSet.add(): dialog cannot be None and must be a Dialog or derived class."
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
if dialog.id in self._dialogs:
|
|
101
|
+
raise TypeError(
|
|
102
|
+
"DialogSet.add(): A dialog with an id of '%s' already added."
|
|
103
|
+
% dialog.id
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
self._dialogs[dialog.id] = dialog
|
|
107
|
+
self._version = None
|
|
108
|
+
|
|
109
|
+
return self
|
|
110
|
+
|
|
111
|
+
async def create_context(self, turn_context: TurnContext) -> "DialogContext":
|
|
112
|
+
"""Creates a DialogContext for this set using the given TurnContext.
|
|
113
|
+
|
|
114
|
+
Loads persisted dialog state via the StatePropertyAccessor provided at construction
|
|
115
|
+
and wraps it in a new DialogContext. Raises RuntimeError if the set was created
|
|
116
|
+
without a StatePropertyAccessor (e.g. from within a ComponentDialog).
|
|
117
|
+
|
|
118
|
+
:param turn_context: The current turn context.
|
|
119
|
+
:return: A DialogContext backed by the loaded dialog state.
|
|
120
|
+
"""
|
|
121
|
+
# This import prevents circular dependency issues
|
|
122
|
+
# pylint: disable=import-outside-toplevel
|
|
123
|
+
from .dialog_context import DialogContext
|
|
124
|
+
|
|
125
|
+
if turn_context is None:
|
|
126
|
+
raise TypeError("DialogSet.create_context(): turn_context cannot be None.")
|
|
127
|
+
|
|
128
|
+
if not self._dialog_state:
|
|
129
|
+
raise RuntimeError(
|
|
130
|
+
"DialogSet.create_context(): DialogSet created with a null IStatePropertyAccessor."
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
from typing import cast as _cast # pylint: disable=import-outside-toplevel
|
|
134
|
+
|
|
135
|
+
state: DialogState = _cast(
|
|
136
|
+
DialogState,
|
|
137
|
+
await self._dialog_state.get(turn_context, lambda: DialogState()),
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
return DialogContext(self, turn_context, state)
|
|
141
|
+
|
|
142
|
+
async def find(self, dialog_id: str | None) -> Dialog | None:
|
|
143
|
+
"""
|
|
144
|
+
Finds a dialog that was previously added to the set using add(dialog)
|
|
145
|
+
:param dialog_id: ID of the dialog/prompt to look up.
|
|
146
|
+
:return: The dialog if found, otherwise null.
|
|
147
|
+
"""
|
|
148
|
+
if not dialog_id:
|
|
149
|
+
raise TypeError("DialogSet.find(): dialog_id cannot be None.")
|
|
150
|
+
|
|
151
|
+
if dialog_id in self._dialogs:
|
|
152
|
+
return self._dialogs[dialog_id]
|
|
153
|
+
|
|
154
|
+
return None
|
|
155
|
+
|
|
156
|
+
def find_dialog(self, dialog_id: str | None) -> Dialog | None:
|
|
157
|
+
"""
|
|
158
|
+
Finds a dialog that was previously added to the set using add(dialog).
|
|
159
|
+
Synchronous version of find().
|
|
160
|
+
:param dialog_id: ID of the dialog/prompt to look up.
|
|
161
|
+
:return: The dialog if found, otherwise null.
|
|
162
|
+
"""
|
|
163
|
+
if not dialog_id:
|
|
164
|
+
raise TypeError("DialogSet.find_dialog(): dialog_id cannot be None.")
|
|
165
|
+
|
|
166
|
+
if dialog_id in self._dialogs:
|
|
167
|
+
return self._dialogs[dialog_id]
|
|
168
|
+
|
|
169
|
+
return None
|
|
170
|
+
|
|
171
|
+
def __str__(self):
|
|
172
|
+
if not self._dialogs:
|
|
173
|
+
return "dialog set empty!"
|
|
174
|
+
return " ".join(map(str, self._dialogs.keys()))
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
|
|
6
|
+
from .models.dialog_instance import DialogInstance
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class DialogState:
|
|
11
|
+
"""
|
|
12
|
+
Contains state information for the dialog stack.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
dialog_stack: list[DialogInstance] = field(default_factory=list)
|
|
16
|
+
|
|
17
|
+
def __str__(self):
|
|
18
|
+
if not self.dialog_stack:
|
|
19
|
+
return "dialog stack empty!"
|
|
20
|
+
return " ".join(map(str, self.dialog_stack))
|
|
@@ -0,0 +1,24 @@
|
|
|
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 .dialog_path import DialogPath
|
|
9
|
+
from .dialog_state_manager import DialogStateManager
|
|
10
|
+
from .dialog_state_manager_configuration import DialogStateManagerConfiguration
|
|
11
|
+
from .component_memory_scopes_base import ComponentMemoryScopesBase
|
|
12
|
+
from .component_path_resolvers_base import ComponentPathResolversBase
|
|
13
|
+
from .path_resolver_base import PathResolverBase
|
|
14
|
+
from . import scope_path
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"DialogPath",
|
|
18
|
+
"DialogStateManager",
|
|
19
|
+
"DialogStateManagerConfiguration",
|
|
20
|
+
"ComponentMemoryScopesBase",
|
|
21
|
+
"ComponentPathResolversBase",
|
|
22
|
+
"PathResolverBase",
|
|
23
|
+
"scope_path",
|
|
24
|
+
]
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
|
|
4
|
+
from abc import ABC, abstractmethod
|
|
5
|
+
from collections.abc import Iterable
|
|
6
|
+
|
|
7
|
+
from .scopes.memory_scope import MemoryScope
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ComponentMemoryScopesBase(ABC):
|
|
11
|
+
|
|
12
|
+
@abstractmethod
|
|
13
|
+
def get_memory_scopes(self) -> Iterable[MemoryScope]:
|
|
14
|
+
raise NotImplementedError()
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
from abc import ABC, abstractmethod
|
|
6
|
+
from collections.abc import Iterable
|
|
7
|
+
|
|
8
|
+
from .path_resolver_base import PathResolverBase
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ComponentPathResolversBase(ABC):
|
|
12
|
+
|
|
13
|
+
@abstractmethod
|
|
14
|
+
def get_path_resolvers(self) -> Iterable[PathResolverBase]:
|
|
15
|
+
raise NotImplementedError()
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class DialogPath:
|
|
6
|
+
# Counter of emitted events.
|
|
7
|
+
EVENT_COUNTER = "dialog.eventCounter"
|
|
8
|
+
|
|
9
|
+
# Currently expected properties.
|
|
10
|
+
EXPECTED_PROPERTIES = "dialog.expectedProperties"
|
|
11
|
+
|
|
12
|
+
# Default operation to use for entities where there is no identified operation entity.
|
|
13
|
+
DEFAULT_OPERATION = "dialog.defaultOperation"
|
|
14
|
+
|
|
15
|
+
# Last surfaced entity ambiguity event.
|
|
16
|
+
LAST_EVENT = "dialog.lastEvent"
|
|
17
|
+
|
|
18
|
+
# Currently required properties.
|
|
19
|
+
REQUIRED_PROPERTIES = "dialog.requiredProperties"
|
|
20
|
+
|
|
21
|
+
# Number of retries for the current Ask.
|
|
22
|
+
RETRIES = "dialog.retries"
|
|
23
|
+
|
|
24
|
+
# Last intent.
|
|
25
|
+
LAST_INTENT = "dialog.lastIntent"
|
|
26
|
+
|
|
27
|
+
# Last trigger event: defined in FormEvent, ask, clarifyEntity etc..
|
|
28
|
+
LAST_TRIGGER_EVENT = "dialog.lastTriggerEvent"
|
|
29
|
+
|
|
30
|
+
@staticmethod
|
|
31
|
+
def get_property_name(prop: str) -> str:
|
|
32
|
+
"""Get the property name without the 'dialog.' prefix, if it exists."""
|
|
33
|
+
return prop.removeprefix("dialog.")
|