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,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,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
|
+
]
|