rasa-pro 3.11.15__py3-none-any.whl → 3.11.17__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of rasa-pro might be problematic. Click here for more details.
- rasa/core/policies/enterprise_search_policy.py +196 -73
- rasa/core/processor.py +50 -5
- rasa/dialogue_understanding/commands/cancel_flow_command.py +3 -2
- rasa/dialogue_understanding/commands/pattern_to_command_mapping.py +45 -0
- rasa/dialogue_understanding/commands/set_slot_command.py +8 -0
- rasa/dialogue_understanding/commands/utils.py +24 -43
- rasa/dialogue_understanding/generator/nlu_command_adapter.py +1 -1
- rasa/dialogue_understanding/processor/command_processor.py +18 -20
- rasa/dialogue_understanding/stack/frames/flow_stack_frame.py +17 -4
- rasa/dialogue_understanding/stack/utils.py +14 -7
- rasa/e2e_test/e2e_test_coverage_report.py +1 -1
- rasa/shared/core/flows/constants.py +2 -0
- rasa/shared/core/flows/flow.py +127 -11
- rasa/shared/core/flows/flows_list.py +18 -1
- rasa/shared/core/flows/steps/link.py +7 -2
- rasa/version.py +1 -1
- {rasa_pro-3.11.15.dist-info → rasa_pro-3.11.17.dist-info}/METADATA +1 -1
- {rasa_pro-3.11.15.dist-info → rasa_pro-3.11.17.dist-info}/RECORD +21 -19
- {rasa_pro-3.11.15.dist-info → rasa_pro-3.11.17.dist-info}/NOTICE +0 -0
- {rasa_pro-3.11.15.dist-info → rasa_pro-3.11.17.dist-info}/WHEEL +0 -0
- {rasa_pro-3.11.15.dist-info → rasa_pro-3.11.17.dist-info}/entry_points.txt +0 -0
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import glob
|
|
1
2
|
import importlib.resources
|
|
2
3
|
import json
|
|
4
|
+
import os.path
|
|
3
5
|
import re
|
|
4
|
-
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Text
|
|
6
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Text, Tuple
|
|
5
7
|
import dotenv
|
|
6
8
|
import structlog
|
|
7
9
|
from jinja2 import Template
|
|
@@ -148,6 +150,8 @@ DEFAULT_ENTERPRISE_SEARCH_PROMPT_WITH_CITATION_TEMPLATE = importlib.resources.re
|
|
|
148
150
|
"rasa.core.policies", "enterprise_search_prompt_with_citation_template.jinja2"
|
|
149
151
|
)
|
|
150
152
|
|
|
153
|
+
_ENTERPRISE_SEARCH_CITATION_PATTERN = re.compile(r"\[([^\]]+)\]")
|
|
154
|
+
|
|
151
155
|
|
|
152
156
|
class VectorStoreConnectionError(RasaException):
|
|
153
157
|
"""Exception raised for errors in connecting to the vector store."""
|
|
@@ -323,9 +327,11 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
|
|
|
323
327
|
|
|
324
328
|
if store_type == DEFAULT_VECTOR_STORE_TYPE:
|
|
325
329
|
logger.info("enterprise_search_policy.train.faiss")
|
|
330
|
+
docs_folder = self.vector_store_config.get(SOURCE_PROPERTY)
|
|
331
|
+
self._validate_documents_folder(docs_folder)
|
|
326
332
|
with self._model_storage.write_to(self._resource) as path:
|
|
327
333
|
self.vector_store = FAISS_Store(
|
|
328
|
-
docs_folder=
|
|
334
|
+
docs_folder=docs_folder,
|
|
329
335
|
embeddings=embeddings,
|
|
330
336
|
index_path=path,
|
|
331
337
|
create_index=True,
|
|
@@ -685,6 +691,33 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
|
|
|
685
691
|
result[domain.index_for_action(action_name)] = score # type: ignore[assignment]
|
|
686
692
|
return result
|
|
687
693
|
|
|
694
|
+
@classmethod
|
|
695
|
+
def _validate_documents_folder(cls, docs_folder: str) -> None:
|
|
696
|
+
if not os.path.exists(docs_folder) or not os.path.isdir(docs_folder):
|
|
697
|
+
error_message = (
|
|
698
|
+
f"Document source directory does not exist or is not a "
|
|
699
|
+
f"directory: '{docs_folder}'. "
|
|
700
|
+
"Please specify a valid path to the documents source directory in the "
|
|
701
|
+
"vector_store configuration."
|
|
702
|
+
)
|
|
703
|
+
logger.error(
|
|
704
|
+
"enterprise_search_policy.train.faiss.invalid_source_directory",
|
|
705
|
+
message=error_message,
|
|
706
|
+
)
|
|
707
|
+
print_error_and_exit(error_message)
|
|
708
|
+
|
|
709
|
+
docs = glob.glob(os.path.join(docs_folder, "*.txt"), recursive=True)
|
|
710
|
+
if not docs or len(docs) < 1:
|
|
711
|
+
error_message = (
|
|
712
|
+
f"Document source directory is empty: '{docs_folder}'. "
|
|
713
|
+
"Please add documents to this directory or specify a different one."
|
|
714
|
+
)
|
|
715
|
+
logger.error(
|
|
716
|
+
"enterprise_search_policy.train.faiss.source_directory_empty",
|
|
717
|
+
message=error_message,
|
|
718
|
+
)
|
|
719
|
+
print_error_and_exit(error_message)
|
|
720
|
+
|
|
688
721
|
@classmethod
|
|
689
722
|
def load(
|
|
690
723
|
cls,
|
|
@@ -695,7 +728,6 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
|
|
|
695
728
|
**kwargs: Any,
|
|
696
729
|
) -> "EnterpriseSearchPolicy":
|
|
697
730
|
"""Loads a trained policy (see parent class for full docstring)."""
|
|
698
|
-
|
|
699
731
|
# Perform health checks for both LLM and embeddings client configs
|
|
700
732
|
cls._perform_health_checks(config, "enterprise_search_policy.load")
|
|
701
733
|
|
|
@@ -759,7 +791,7 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
|
|
|
759
791
|
return None
|
|
760
792
|
|
|
761
793
|
source = merged_config.get(VECTOR_STORE_PROPERTY, {}).get(SOURCE_PROPERTY)
|
|
762
|
-
if not source:
|
|
794
|
+
if not source or not os.path.exists(source) or not os.path.isdir(source):
|
|
763
795
|
return None
|
|
764
796
|
|
|
765
797
|
docs = FAISS_Store.load_documents(source)
|
|
@@ -794,10 +826,18 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
|
|
|
794
826
|
|
|
795
827
|
@staticmethod
|
|
796
828
|
def post_process_citations(llm_answer: str) -> str:
|
|
797
|
-
"""Post-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
829
|
+
"""Post-processes the LLM answer to correctly number and sort citations and
|
|
830
|
+
sources.
|
|
831
|
+
|
|
832
|
+
- Handles both single `[1]` and grouped `[1, 3]` citations.
|
|
833
|
+
- Rewrites the numbers in square brackets in the answer text to start from 1
|
|
834
|
+
and be sorted within each group.
|
|
835
|
+
- Reorders the sources according to the order of their first appearance
|
|
836
|
+
in the text.
|
|
837
|
+
- Removes citations from the text that point to sources missing from
|
|
838
|
+
the source list.
|
|
839
|
+
- Keeps sources that are not cited in the text, placing them at the end
|
|
840
|
+
of the list.
|
|
801
841
|
|
|
802
842
|
Args:
|
|
803
843
|
llm_answer: The LLM answer.
|
|
@@ -811,77 +851,160 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
|
|
|
811
851
|
|
|
812
852
|
# Split llm_answer into answer and citations
|
|
813
853
|
try:
|
|
814
|
-
|
|
854
|
+
answer_part, sources_part = llm_answer.rsplit("Sources:", 1)
|
|
815
855
|
except ValueError:
|
|
816
|
-
# if there is no "Sources:"
|
|
817
|
-
return llm_answer
|
|
818
|
-
|
|
819
|
-
#
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
856
|
+
# if there is no "Sources:" separator, return the original llm_answer
|
|
857
|
+
return llm_answer.strip()
|
|
858
|
+
|
|
859
|
+
# Parse the sources block to extract valid sources and other lines
|
|
860
|
+
valid_sources, other_source_lines = EnterpriseSearchPolicy._parse_sources_block(
|
|
861
|
+
sources_part
|
|
862
|
+
)
|
|
863
|
+
|
|
864
|
+
# Find all unique, valid citations in the answer text in their order
|
|
865
|
+
# of appearance
|
|
866
|
+
cited_order = EnterpriseSearchPolicy._get_cited_order(
|
|
867
|
+
answer_part, valid_sources
|
|
868
|
+
)
|
|
869
|
+
|
|
870
|
+
# Create a mapping from the old source numbers to the new, sequential numbers.
|
|
871
|
+
# For example, if the citation order in the text was [3, 1, 2], this map
|
|
872
|
+
# becomes {3: 1, 1: 2, 2: 3}. This allows for a quick lookup when rewriting
|
|
873
|
+
# the citations
|
|
874
|
+
renumbering_map = {
|
|
875
|
+
old_num: new_num + 1 for new_num, old_num in enumerate(cited_order)
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
# Rewrite the citations in the answer text based on the renumbering map
|
|
879
|
+
processed_answer = EnterpriseSearchPolicy._rewrite_answer_citations(
|
|
880
|
+
answer_part, renumbering_map
|
|
881
|
+
)
|
|
882
|
+
|
|
883
|
+
# Build the new list of sources
|
|
884
|
+
new_sources_list = EnterpriseSearchPolicy._build_final_sources_list(
|
|
885
|
+
cited_order,
|
|
886
|
+
renumbering_map,
|
|
887
|
+
valid_sources,
|
|
888
|
+
other_source_lines,
|
|
889
|
+
)
|
|
825
890
|
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
match, f"{', '.join(map(str, new_indices))}"
|
|
852
|
-
)
|
|
853
|
-
else:
|
|
854
|
-
old_index = int(match.strip("[].,:;?!"))
|
|
855
|
-
new_index = renumber_mapping.get(old_index)
|
|
856
|
-
if not new_index:
|
|
857
|
-
continue
|
|
858
|
-
|
|
859
|
-
word = word.replace(str(old_index), str(new_index))
|
|
860
|
-
new_answer.append(word)
|
|
861
|
-
|
|
862
|
-
# join the words
|
|
863
|
-
joined_answer = " ".join(new_answer)
|
|
864
|
-
joined_answer += "\nSources:\n"
|
|
865
|
-
|
|
866
|
-
new_sources: List[str] = []
|
|
867
|
-
|
|
868
|
-
for line in citations.split("\n"):
|
|
869
|
-
pattern = r"(?<=\[)\d+"
|
|
870
|
-
match = re.search(pattern, line)
|
|
891
|
+
if len(new_sources_list) > 0:
|
|
892
|
+
processed_answer += "\nSources:\n" + "\n".join(new_sources_list)
|
|
893
|
+
|
|
894
|
+
return processed_answer
|
|
895
|
+
|
|
896
|
+
@staticmethod
|
|
897
|
+
def _parse_sources_block(sources_part: str) -> Tuple[Dict[int, str], List[str]]:
|
|
898
|
+
"""Parses the sources block from the LLM response.
|
|
899
|
+
Returns a tuple containing:
|
|
900
|
+
- A dictionary of valid sources matching the "[1] ..." format,
|
|
901
|
+
where the key is the source number
|
|
902
|
+
- A list of other source lines that do not match the specified format
|
|
903
|
+
"""
|
|
904
|
+
valid_sources: Dict[int, str] = {}
|
|
905
|
+
other_source_lines: List[str] = []
|
|
906
|
+
source_line_pattern = re.compile(r"^\s*\[(\d+)\](.*)")
|
|
907
|
+
|
|
908
|
+
source_lines = sources_part.strip().split("\n")
|
|
909
|
+
|
|
910
|
+
for line in source_lines:
|
|
911
|
+
line = line.strip()
|
|
912
|
+
if not line:
|
|
913
|
+
continue
|
|
914
|
+
|
|
915
|
+
match = source_line_pattern.match(line)
|
|
871
916
|
if match:
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
917
|
+
num = int(match.group(1))
|
|
918
|
+
valid_sources[num] = line
|
|
919
|
+
else:
|
|
920
|
+
other_source_lines.append(line)
|
|
921
|
+
|
|
922
|
+
return valid_sources, other_source_lines
|
|
923
|
+
|
|
924
|
+
@staticmethod
|
|
925
|
+
def _get_cited_order(
|
|
926
|
+
answer_part: str, available_sources: Dict[int, str]
|
|
927
|
+
) -> List[int]:
|
|
928
|
+
"""Find all unique, valid citations in the answer text in their order
|
|
929
|
+
# of appearance
|
|
930
|
+
"""
|
|
931
|
+
cited_order: List[int] = []
|
|
932
|
+
seen_indices = set()
|
|
933
|
+
|
|
934
|
+
for match in _ENTERPRISE_SEARCH_CITATION_PATTERN.finditer(answer_part):
|
|
935
|
+
content = match.group(1)
|
|
936
|
+
indices_str = [s.strip() for s in content.split(",")]
|
|
937
|
+
for index_str in indices_str:
|
|
938
|
+
if index_str.isdigit():
|
|
939
|
+
index = int(index_str)
|
|
940
|
+
if index in available_sources and index not in seen_indices:
|
|
941
|
+
cited_order.append(index)
|
|
942
|
+
seen_indices.add(index)
|
|
943
|
+
|
|
944
|
+
return cited_order
|
|
945
|
+
|
|
946
|
+
@staticmethod
|
|
947
|
+
def _rewrite_answer_citations(
|
|
948
|
+
answer_part: str, renumber_map: Dict[int, int]
|
|
949
|
+
) -> str:
|
|
950
|
+
"""Rewrites the citations in the answer text based on the renumbering map."""
|
|
951
|
+
|
|
952
|
+
def replacer(match: re.Match) -> str:
|
|
953
|
+
content = match.group(1)
|
|
954
|
+
old_indices_str = [s.strip() for s in content.split(",")]
|
|
955
|
+
new_indices = [
|
|
956
|
+
renumber_map[int(s)]
|
|
957
|
+
for s in old_indices_str
|
|
958
|
+
if s.isdigit() and int(s) in renumber_map
|
|
959
|
+
]
|
|
960
|
+
if not new_indices:
|
|
961
|
+
return ""
|
|
962
|
+
|
|
963
|
+
return f"[{', '.join(map(str, sorted(list(set(new_indices)))))}]"
|
|
964
|
+
|
|
965
|
+
processed_answer = _ENTERPRISE_SEARCH_CITATION_PATTERN.sub(
|
|
966
|
+
replacer, answer_part
|
|
967
|
+
)
|
|
968
|
+
|
|
969
|
+
# Clean up formatting after replacements
|
|
970
|
+
processed_answer = re.sub(r"\s+([,.?])", r"\1", processed_answer)
|
|
971
|
+
processed_answer = processed_answer.replace("[]", " ")
|
|
972
|
+
processed_answer = re.sub(r"\s+", " ", processed_answer)
|
|
973
|
+
processed_answer = processed_answer.strip()
|
|
974
|
+
|
|
975
|
+
return processed_answer
|
|
976
|
+
|
|
977
|
+
@staticmethod
|
|
978
|
+
def _build_final_sources_list(
|
|
979
|
+
cited_order: List[int],
|
|
980
|
+
renumbering_map: Dict[int, int],
|
|
981
|
+
valid_sources: Dict[int, str],
|
|
982
|
+
other_source_lines: List[str],
|
|
983
|
+
) -> List[str]:
|
|
984
|
+
"""Builds the final list of sources based on the cited order and
|
|
985
|
+
renumbering map.
|
|
986
|
+
"""
|
|
987
|
+
new_sources_list: List[str] = []
|
|
988
|
+
|
|
989
|
+
# First, add the sorted, used sources
|
|
990
|
+
for old_num in cited_order:
|
|
991
|
+
new_num = renumbering_map[old_num]
|
|
992
|
+
source_line = valid_sources[old_num]
|
|
993
|
+
new_sources_list.append(
|
|
994
|
+
source_line.replace(f"[{old_num}]", f"[{new_num}]", 1)
|
|
995
|
+
)
|
|
876
996
|
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
997
|
+
# Then, add the unused but validly numbered sources
|
|
998
|
+
used_source_nums = set(cited_order)
|
|
999
|
+
# Sort by number to ensure a consistent order for uncited sources
|
|
1000
|
+
for num, line in sorted(valid_sources.items()):
|
|
1001
|
+
if num not in used_source_nums:
|
|
1002
|
+
new_sources_list.append(line)
|
|
881
1003
|
|
|
882
|
-
|
|
1004
|
+
# Finally, add any other source lines
|
|
1005
|
+
new_sources_list.extend(other_source_lines)
|
|
883
1006
|
|
|
884
|
-
return
|
|
1007
|
+
return new_sources_list
|
|
885
1008
|
|
|
886
1009
|
@classmethod
|
|
887
1010
|
def _perform_health_checks(
|
rasa/core/processor.py
CHANGED
|
@@ -17,6 +17,9 @@ from rasa.dialogue_understanding.commands import (
|
|
|
17
17
|
NoopCommand,
|
|
18
18
|
SetSlotCommand,
|
|
19
19
|
CannotHandleCommand,
|
|
20
|
+
SessionStartCommand,
|
|
21
|
+
SessionEndCommand,
|
|
22
|
+
RestartCommand,
|
|
20
23
|
)
|
|
21
24
|
from rasa.engine import loader
|
|
22
25
|
from rasa.engine.constants import (
|
|
@@ -855,19 +858,61 @@ class MessageProcessor:
|
|
|
855
858
|
tracker.has_coexistence_routing_slot
|
|
856
859
|
and tracker.get_slot(ROUTE_TO_CALM_SLOT) is None
|
|
857
860
|
):
|
|
858
|
-
#
|
|
859
|
-
#
|
|
860
|
-
#
|
|
861
|
-
#
|
|
861
|
+
# If we are currently not routing to either CALM or DM1:
|
|
862
|
+
# - Sticky route to CALM if there are any commands
|
|
863
|
+
# from the trigger intent parsing
|
|
864
|
+
# - Sticky route to DM1 if there are no commands present
|
|
865
|
+
route_to_calm_slot_value = self._determine_route_to_calm_slot_value(
|
|
866
|
+
nlu_adapted_commands
|
|
867
|
+
)
|
|
862
868
|
commands += [
|
|
863
869
|
SetSlotCommand(
|
|
864
|
-
ROUTE_TO_CALM_SLOT,
|
|
870
|
+
ROUTE_TO_CALM_SLOT, route_to_calm_slot_value
|
|
865
871
|
).as_dict()
|
|
866
872
|
]
|
|
867
873
|
|
|
868
874
|
parse_data[COMMANDS] = commands
|
|
869
875
|
return parse_data
|
|
870
876
|
|
|
877
|
+
def _determine_route_to_calm_slot_value(
|
|
878
|
+
self, nlu_adapted_commands: List[Dict[str, Any]]
|
|
879
|
+
) -> Optional[bool]:
|
|
880
|
+
"""Determines what value should be assigned to `ROUTE_TO_CALM_SLOT`.
|
|
881
|
+
|
|
882
|
+
Returns:
|
|
883
|
+
- True: If any command other than:
|
|
884
|
+
- SessionStartCommand
|
|
885
|
+
- SessionEndCommand
|
|
886
|
+
- RestartCommand
|
|
887
|
+
is present.
|
|
888
|
+
- None: If only ignored system commands are present.
|
|
889
|
+
- False If no commands at all.
|
|
890
|
+
"""
|
|
891
|
+
system_commands_to_ignore = [
|
|
892
|
+
SessionStartCommand.command(),
|
|
893
|
+
SessionEndCommand.command(),
|
|
894
|
+
RestartCommand.command(),
|
|
895
|
+
]
|
|
896
|
+
|
|
897
|
+
# Exclude the system commands, as it doesn't originate from the user's
|
|
898
|
+
# input intent and shouldn't influence the decision for setting
|
|
899
|
+
# ROUTE_TO_CALM_SLOT.
|
|
900
|
+
intent_triggered_commands = [
|
|
901
|
+
command
|
|
902
|
+
for command in nlu_adapted_commands
|
|
903
|
+
if command.get("command") not in system_commands_to_ignore
|
|
904
|
+
]
|
|
905
|
+
|
|
906
|
+
if len(intent_triggered_commands) > 0:
|
|
907
|
+
# There are commands other than system commands present - route to CALM
|
|
908
|
+
return True
|
|
909
|
+
elif len(nlu_adapted_commands) > 0:
|
|
910
|
+
# Only system command is present — defer routing decision
|
|
911
|
+
return None
|
|
912
|
+
else:
|
|
913
|
+
# No commands at all — route to DM1
|
|
914
|
+
return False
|
|
915
|
+
|
|
871
916
|
def _update_full_retrieval_intent(self, parse_data: Dict[Text, Any]) -> None:
|
|
872
917
|
"""Update the parse data with the full retrieval intent.
|
|
873
918
|
|
|
@@ -88,8 +88,9 @@ class CancelFlowCommand(Command):
|
|
|
88
88
|
original_stack = original_tracker.stack
|
|
89
89
|
|
|
90
90
|
applied_events: List[Event] = []
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
user_frame = top_user_flow_frame(
|
|
92
|
+
original_stack, ignore_call_and_link_frames=False
|
|
93
|
+
)
|
|
93
94
|
current_flow = user_frame.flow(all_flows) if user_frame else None
|
|
94
95
|
|
|
95
96
|
if not current_flow:
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from typing import Dict, Type
|
|
2
|
+
|
|
3
|
+
from rasa.dialogue_understanding.commands import (
|
|
4
|
+
CancelFlowCommand,
|
|
5
|
+
CannotHandleCommand,
|
|
6
|
+
ChitChatAnswerCommand,
|
|
7
|
+
Command,
|
|
8
|
+
HumanHandoffCommand,
|
|
9
|
+
KnowledgeAnswerCommand,
|
|
10
|
+
SessionStartCommand,
|
|
11
|
+
SkipQuestionCommand,
|
|
12
|
+
RestartCommand,
|
|
13
|
+
)
|
|
14
|
+
from rasa.dialogue_understanding.commands.user_silence_command import UserSilenceCommand
|
|
15
|
+
from rasa.dialogue_understanding.patterns.cancel import CancelPatternFlowStackFrame
|
|
16
|
+
from rasa.dialogue_understanding.patterns.cannot_handle import (
|
|
17
|
+
CannotHandlePatternFlowStackFrame,
|
|
18
|
+
)
|
|
19
|
+
from rasa.dialogue_understanding.patterns.chitchat import ChitchatPatternFlowStackFrame
|
|
20
|
+
from rasa.dialogue_understanding.patterns.human_handoff import (
|
|
21
|
+
HumanHandoffPatternFlowStackFrame,
|
|
22
|
+
)
|
|
23
|
+
from rasa.dialogue_understanding.patterns.restart import RestartPatternFlowStackFrame
|
|
24
|
+
from rasa.dialogue_understanding.patterns.search import SearchPatternFlowStackFrame
|
|
25
|
+
from rasa.dialogue_understanding.patterns.session_start import (
|
|
26
|
+
SessionStartPatternFlowStackFrame,
|
|
27
|
+
)
|
|
28
|
+
from rasa.dialogue_understanding.patterns.skip_question import (
|
|
29
|
+
SkipQuestionPatternFlowStackFrame,
|
|
30
|
+
)
|
|
31
|
+
from rasa.dialogue_understanding.patterns.user_silence import (
|
|
32
|
+
UserSilencePatternFlowStackFrame,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
triggerable_pattern_to_command_class: Dict[str, Type[Command]] = {
|
|
36
|
+
SessionStartPatternFlowStackFrame.flow_id: SessionStartCommand,
|
|
37
|
+
UserSilencePatternFlowStackFrame.flow_id: UserSilenceCommand,
|
|
38
|
+
CancelPatternFlowStackFrame.flow_id: CancelFlowCommand,
|
|
39
|
+
ChitchatPatternFlowStackFrame.flow_id: ChitChatAnswerCommand,
|
|
40
|
+
HumanHandoffPatternFlowStackFrame.flow_id: HumanHandoffCommand,
|
|
41
|
+
SearchPatternFlowStackFrame.flow_id: KnowledgeAnswerCommand,
|
|
42
|
+
SkipQuestionPatternFlowStackFrame.flow_id: SkipQuestionCommand,
|
|
43
|
+
CannotHandlePatternFlowStackFrame.flow_id: CannotHandleCommand,
|
|
44
|
+
RestartPatternFlowStackFrame.flow_id: RestartCommand,
|
|
45
|
+
}
|
|
@@ -6,6 +6,9 @@ from typing import Any, Dict, List
|
|
|
6
6
|
|
|
7
7
|
import structlog
|
|
8
8
|
from rasa.dialogue_understanding.commands import Command
|
|
9
|
+
from rasa.dialogue_understanding.commands.utils import (
|
|
10
|
+
find_default_flows_collecting_slot,
|
|
11
|
+
)
|
|
9
12
|
from rasa.dialogue_understanding.patterns.collect_information import (
|
|
10
13
|
CollectInformationPatternFlowStackFrame,
|
|
11
14
|
)
|
|
@@ -135,6 +138,11 @@ class SetSlotCommand(Command):
|
|
|
135
138
|
):
|
|
136
139
|
# Get the other predicted flows from the most recent message on the tracker.
|
|
137
140
|
predicted_flows = get_flows_predicted_to_start_from_tracker(tracker)
|
|
141
|
+
if not predicted_flows:
|
|
142
|
+
# If no predicted flows, check for default flows collecting the slot.
|
|
143
|
+
predicted_flows = find_default_flows_collecting_slot(
|
|
144
|
+
self.name, all_flows
|
|
145
|
+
)
|
|
138
146
|
use_slot_fill = any(
|
|
139
147
|
step.collect == self.name and not step.ask_before_filling
|
|
140
148
|
for flow in all_flows.underlying_flows
|
|
@@ -1,45 +1,26 @@
|
|
|
1
|
-
from typing import
|
|
1
|
+
from typing import List
|
|
2
|
+
from rasa.shared.core.flows import FlowsList
|
|
2
3
|
|
|
3
|
-
from rasa.dialogue_understanding.commands import (
|
|
4
|
-
CancelFlowCommand,
|
|
5
|
-
CannotHandleCommand,
|
|
6
|
-
ChitChatAnswerCommand,
|
|
7
|
-
Command,
|
|
8
|
-
HumanHandoffCommand,
|
|
9
|
-
KnowledgeAnswerCommand,
|
|
10
|
-
SessionStartCommand,
|
|
11
|
-
SkipQuestionCommand,
|
|
12
|
-
RestartCommand,
|
|
13
|
-
)
|
|
14
|
-
from rasa.dialogue_understanding.commands.user_silence_command import UserSilenceCommand
|
|
15
|
-
from rasa.dialogue_understanding.patterns.cancel import CancelPatternFlowStackFrame
|
|
16
|
-
from rasa.dialogue_understanding.patterns.cannot_handle import (
|
|
17
|
-
CannotHandlePatternFlowStackFrame,
|
|
18
|
-
)
|
|
19
|
-
from rasa.dialogue_understanding.patterns.chitchat import ChitchatPatternFlowStackFrame
|
|
20
|
-
from rasa.dialogue_understanding.patterns.human_handoff import (
|
|
21
|
-
HumanHandoffPatternFlowStackFrame,
|
|
22
|
-
)
|
|
23
|
-
from rasa.dialogue_understanding.patterns.restart import RestartPatternFlowStackFrame
|
|
24
|
-
from rasa.dialogue_understanding.patterns.search import SearchPatternFlowStackFrame
|
|
25
|
-
from rasa.dialogue_understanding.patterns.session_start import (
|
|
26
|
-
SessionStartPatternFlowStackFrame,
|
|
27
|
-
)
|
|
28
|
-
from rasa.dialogue_understanding.patterns.skip_question import (
|
|
29
|
-
SkipQuestionPatternFlowStackFrame,
|
|
30
|
-
)
|
|
31
|
-
from rasa.dialogue_understanding.patterns.user_silence import (
|
|
32
|
-
UserSilencePatternFlowStackFrame,
|
|
33
|
-
)
|
|
34
4
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
5
|
+
def find_default_flows_collecting_slot(
|
|
6
|
+
slot_name: str, all_flows: FlowsList
|
|
7
|
+
) -> List[str]:
|
|
8
|
+
"""Find default flows that have collect steps matching the specified slot name.
|
|
9
|
+
|
|
10
|
+
Args:
|
|
11
|
+
slot_name: The name of the slot to search for.
|
|
12
|
+
all_flows: All flows in the assistant.
|
|
13
|
+
|
|
14
|
+
Returns:
|
|
15
|
+
List of flow IDs for default flows that collect the specified slot
|
|
16
|
+
without asking before filling.
|
|
17
|
+
"""
|
|
18
|
+
return [
|
|
19
|
+
flow.id
|
|
20
|
+
for flow in all_flows.underlying_flows
|
|
21
|
+
if flow.is_rasa_default_flow
|
|
22
|
+
and any(
|
|
23
|
+
step.collect == slot_name and not step.ask_before_filling
|
|
24
|
+
for step in flow.get_collect_steps()
|
|
25
|
+
)
|
|
26
|
+
]
|
|
@@ -8,7 +8,7 @@ from rasa.dialogue_understanding.commands import (
|
|
|
8
8
|
SetSlotCommand,
|
|
9
9
|
)
|
|
10
10
|
from rasa.dialogue_understanding.commands.set_slot_command import SetSlotExtractor
|
|
11
|
-
from rasa.dialogue_understanding.commands.
|
|
11
|
+
from rasa.dialogue_understanding.commands.pattern_to_command_mapping import (
|
|
12
12
|
triggerable_pattern_to_command_class,
|
|
13
13
|
)
|
|
14
14
|
from rasa.dialogue_understanding.generator import CommandGenerator
|
|
@@ -52,12 +52,6 @@ from rasa.shared.nlu.constants import COMMANDS
|
|
|
52
52
|
|
|
53
53
|
structlogger = structlog.get_logger()
|
|
54
54
|
|
|
55
|
-
CANNOT_HANDLE_REASON = (
|
|
56
|
-
"A command generator attempted to set a slot "
|
|
57
|
-
"with a value extracted by an extractor "
|
|
58
|
-
"that is incompatible with the slot mapping type."
|
|
59
|
-
)
|
|
60
|
-
|
|
61
55
|
|
|
62
56
|
def contains_command(commands: List[Command], typ: Type[Command]) -> bool:
|
|
63
57
|
"""Check if a list of commands contains a command of a given type.
|
|
@@ -432,9 +426,9 @@ def clean_up_commands(
|
|
|
432
426
|
# when coexistence is enabled, by default there will be a SetSlotCommand
|
|
433
427
|
# for the ROUTE_TO_CALM_SLOT slot.
|
|
434
428
|
if tracker.has_coexistence_routing_slot and len(clean_commands) > 2:
|
|
435
|
-
clean_commands =
|
|
429
|
+
clean_commands = filter_cannot_handle_command(clean_commands)
|
|
436
430
|
elif not tracker.has_coexistence_routing_slot and len(clean_commands) > 1:
|
|
437
|
-
clean_commands =
|
|
431
|
+
clean_commands = filter_cannot_handle_command(clean_commands)
|
|
438
432
|
|
|
439
433
|
clean_commands = ensure_max_number_of_command_type(
|
|
440
434
|
clean_commands, RepeatBotMessagesCommand, 1
|
|
@@ -534,10 +528,18 @@ def clean_up_slot_command(
|
|
|
534
528
|
"command_processor.clean_up_slot_command.skip_command_slot_not_in_domain",
|
|
535
529
|
command=command,
|
|
536
530
|
)
|
|
531
|
+
resulting_commands.append(
|
|
532
|
+
CannotHandleCommand(
|
|
533
|
+
reason="The slot predicted by the LLM is not defined in the domain."
|
|
534
|
+
)
|
|
535
|
+
)
|
|
537
536
|
return resulting_commands
|
|
538
537
|
|
|
539
538
|
if not should_slot_be_set(slot, command):
|
|
540
|
-
cannot_handle = CannotHandleCommand(
|
|
539
|
+
cannot_handle = CannotHandleCommand(
|
|
540
|
+
reason="A command generator attempted to set a slot with a value extracted "
|
|
541
|
+
"by an extractor that is incompatible with the slot mapping type."
|
|
542
|
+
)
|
|
541
543
|
if cannot_handle not in resulting_commands:
|
|
542
544
|
resulting_commands.append(cannot_handle)
|
|
543
545
|
|
|
@@ -551,9 +553,9 @@ def clean_up_slot_command(
|
|
|
551
553
|
resulting_commands.append(command)
|
|
552
554
|
return resulting_commands
|
|
553
555
|
|
|
554
|
-
if (slot := tracker.slots.get(command.name)) is not None and
|
|
555
|
-
|
|
556
|
-
):
|
|
556
|
+
if (slot := tracker.slots.get(command.name)) is not None and str(
|
|
557
|
+
slot.value
|
|
558
|
+
) == str(command.value):
|
|
557
559
|
# the slot is already set, we don't need to set it again
|
|
558
560
|
structlogger.debug(
|
|
559
561
|
"command_processor.clean_up_slot_command.skip_command_slot_already_set",
|
|
@@ -713,12 +715,12 @@ def should_slot_be_set(slot: Slot, command: SetSlotCommand) -> bool:
|
|
|
713
715
|
return True
|
|
714
716
|
|
|
715
717
|
|
|
716
|
-
def
|
|
718
|
+
def filter_cannot_handle_command(
|
|
717
719
|
clean_commands: List[Command],
|
|
718
720
|
) -> List[Command]:
|
|
719
|
-
"""Filter out a 'cannot handle' command
|
|
721
|
+
"""Filter out a 'cannot handle' command.
|
|
720
722
|
|
|
721
|
-
This is used to filter out a 'cannot handle' command
|
|
723
|
+
This is used to filter out a 'cannot handle' command
|
|
722
724
|
in case other commands are present.
|
|
723
725
|
|
|
724
726
|
Returns:
|
|
@@ -727,9 +729,5 @@ def filter_cannot_handle_command_for_skipped_slots(
|
|
|
727
729
|
return [
|
|
728
730
|
command
|
|
729
731
|
for command in clean_commands
|
|
730
|
-
if not (
|
|
731
|
-
isinstance(command, CannotHandleCommand)
|
|
732
|
-
and command.reason
|
|
733
|
-
and CANNOT_HANDLE_REASON == command.reason
|
|
734
|
-
)
|
|
732
|
+
if not isinstance(command, CannotHandleCommand)
|
|
735
733
|
]
|
|
@@ -50,7 +50,8 @@ class FlowStackFrameType(str, Enum):
|
|
|
50
50
|
typ: The string to create the `FlowStackFrameType` from.
|
|
51
51
|
|
|
52
52
|
Returns:
|
|
53
|
-
|
|
53
|
+
The created `FlowStackFrameType`.
|
|
54
|
+
"""
|
|
54
55
|
if typ is None:
|
|
55
56
|
return FlowStackFrameType.REGULAR
|
|
56
57
|
elif typ == FlowStackFrameType.INTERRUPT.value:
|
|
@@ -104,7 +105,8 @@ class BaseFlowStackFrame(DialogueStackFrame):
|
|
|
104
105
|
all_flows: All flows in the assistant.
|
|
105
106
|
|
|
106
107
|
Returns:
|
|
107
|
-
|
|
108
|
+
The current flow.
|
|
109
|
+
"""
|
|
108
110
|
flow = all_flows.flow_by_id(self.flow_id)
|
|
109
111
|
if not flow:
|
|
110
112
|
# we shouldn't ever end up with a frame that belongs to a non
|
|
@@ -119,9 +121,20 @@ class BaseFlowStackFrame(DialogueStackFrame):
|
|
|
119
121
|
all_flows: All flows in the assistant.
|
|
120
122
|
|
|
121
123
|
Returns:
|
|
122
|
-
The current flow step.
|
|
124
|
+
The current flow step.
|
|
125
|
+
"""
|
|
123
126
|
flow = self.flow(all_flows)
|
|
124
|
-
|
|
127
|
+
|
|
128
|
+
step_id = self.step_id
|
|
129
|
+
# in 3.11.4 we added the flow_id as a prefix to the step_id
|
|
130
|
+
# this causes issues when loading old dialogues as the prefix is missing
|
|
131
|
+
# (see https://rasahq.atlassian.net/jira/software/c/projects/ENG/boards/43?selectedIssue=ENG-1939)
|
|
132
|
+
# so we try to find the step by adding the flow prefix to old step_ids as well
|
|
133
|
+
# TODO: remove this in 4.0.0
|
|
134
|
+
alternative_step_id = f"{self.flow_id}_{self.step_id}"
|
|
135
|
+
|
|
136
|
+
step = flow.step_by_id(step_id) or flow.step_by_id(alternative_step_id)
|
|
137
|
+
|
|
125
138
|
if not step:
|
|
126
139
|
# we shouldn't ever end up with a frame that belongs to a non
|
|
127
140
|
# existing step, but if we do, we should raise an error
|
|
@@ -57,7 +57,9 @@ def top_flow_frame(
|
|
|
57
57
|
return None
|
|
58
58
|
|
|
59
59
|
|
|
60
|
-
def top_user_flow_frame(
|
|
60
|
+
def top_user_flow_frame(
|
|
61
|
+
dialogue_stack: DialogueStack, ignore_call_and_link_frames: bool = True
|
|
62
|
+
) -> Optional[UserFlowStackFrame]:
|
|
61
63
|
"""Returns the topmost user flow frame from the tracker.
|
|
62
64
|
|
|
63
65
|
User flows are flows that are created by developers of an assistant and
|
|
@@ -69,16 +71,19 @@ def top_user_flow_frame(dialogue_stack: DialogueStack) -> Optional[UserFlowStack
|
|
|
69
71
|
|
|
70
72
|
Args:
|
|
71
73
|
dialogue_stack: The dialogue stack to use.
|
|
74
|
+
ignore_call_and_link_frames: Whether to ignore user frames of type `call`
|
|
75
|
+
and `link`. By default, these frames are ignored.
|
|
72
76
|
|
|
73
77
|
Returns:
|
|
74
78
|
The topmost user flow frame from the tracker.
|
|
75
79
|
"""
|
|
76
80
|
for frame in reversed(dialogue_stack.frames):
|
|
77
|
-
if (
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
81
|
+
if isinstance(frame, UserFlowStackFrame):
|
|
82
|
+
if ignore_call_and_link_frames and (
|
|
83
|
+
frame.frame_type == FlowStackFrameType.CALL
|
|
84
|
+
or frame.frame_type == FlowStackFrameType.LINK
|
|
85
|
+
):
|
|
86
|
+
continue
|
|
82
87
|
return frame
|
|
83
88
|
return None
|
|
84
89
|
|
|
@@ -201,7 +206,9 @@ def get_collect_steps_excluding_ask_before_filling_for_active_flow(
|
|
|
201
206
|
All collect steps that are part of the current active flow,
|
|
202
207
|
excluding the collect steps that have to be asked before filling.
|
|
203
208
|
"""
|
|
204
|
-
active_frame = top_user_flow_frame(
|
|
209
|
+
active_frame = top_user_flow_frame(
|
|
210
|
+
dialogue_stack, ignore_call_and_link_frames=False
|
|
211
|
+
)
|
|
205
212
|
if active_frame is None:
|
|
206
213
|
return set()
|
|
207
214
|
active_flow = active_frame.flow(all_flows)
|
|
@@ -21,7 +21,7 @@ from rasa.shared.core.flows.flow_path import FlowPath, FlowPathsList, PathNode
|
|
|
21
21
|
FLOW_NAME_COL_NAME = "Flow Name"
|
|
22
22
|
NUM_STEPS_COL_NAME = "Num Steps"
|
|
23
23
|
MISSING_STEPS_COL_NAME = "Missing Steps"
|
|
24
|
-
LINE_NUMBERS_COL_NAME = "Line Numbers"
|
|
24
|
+
LINE_NUMBERS_COL_NAME = "Line Numbers for Missing Steps"
|
|
25
25
|
COVERAGE_COL_NAME = "Coverage"
|
|
26
26
|
|
|
27
27
|
FLOWS_KEY = "flows"
|
rasa/shared/core/flows/flow.py
CHANGED
|
@@ -4,14 +4,18 @@ import copy
|
|
|
4
4
|
from dataclasses import dataclass, field
|
|
5
5
|
from functools import cached_property
|
|
6
6
|
from pathlib import Path
|
|
7
|
-
from typing import
|
|
7
|
+
from typing import Any, Dict, List, Optional, Set, Text, Tuple, Union
|
|
8
8
|
|
|
9
9
|
import structlog
|
|
10
10
|
from pypred import Predicate
|
|
11
11
|
|
|
12
12
|
import rasa.shared.utils.io
|
|
13
13
|
from rasa.shared.constants import RASA_DEFAULT_FLOW_PATTERN_PREFIX
|
|
14
|
-
from rasa.shared.core.flows.
|
|
14
|
+
from rasa.shared.core.flows.constants import (
|
|
15
|
+
KEY_CALLED_FLOW,
|
|
16
|
+
KEY_LINKED_FLOW,
|
|
17
|
+
)
|
|
18
|
+
from rasa.shared.core.flows.flow_path import FlowPath, FlowPathsList, PathNode
|
|
15
19
|
from rasa.shared.core.flows.flow_step import FlowStep
|
|
16
20
|
from rasa.shared.core.flows.flow_step_links import (
|
|
17
21
|
FlowStepLink,
|
|
@@ -26,6 +30,7 @@ from rasa.shared.core.flows.steps import (
|
|
|
26
30
|
CallFlowStep,
|
|
27
31
|
CollectInformationFlowStep,
|
|
28
32
|
EndFlowStep,
|
|
33
|
+
LinkFlowStep,
|
|
29
34
|
StartFlowStep,
|
|
30
35
|
)
|
|
31
36
|
from rasa.shared.core.flows.steps.constants import (
|
|
@@ -424,6 +429,9 @@ class Flow:
|
|
|
424
429
|
current_path: FlowPath,
|
|
425
430
|
all_paths: FlowPathsList,
|
|
426
431
|
visited_step_ids: Set[str],
|
|
432
|
+
call_stack: Optional[
|
|
433
|
+
List[Tuple[Optional[FlowStep], Optional[Flow], str]]
|
|
434
|
+
] = None,
|
|
427
435
|
) -> None:
|
|
428
436
|
"""Processes the flow steps recursively.
|
|
429
437
|
|
|
@@ -432,19 +440,25 @@ class Flow:
|
|
|
432
440
|
current_path: The current path being constructed.
|
|
433
441
|
all_paths: The list where completed paths are added.
|
|
434
442
|
visited_step_ids: A set of steps that have been visited to avoid cycles.
|
|
443
|
+
call_stack: Tuple list of (flow, path, flow_type) to track path when \
|
|
444
|
+
calling flows through call and link steps.
|
|
435
445
|
|
|
436
446
|
Returns:
|
|
437
447
|
None: This function modifies all_paths in place by appending new paths
|
|
438
448
|
as they are found.
|
|
439
449
|
"""
|
|
450
|
+
if call_stack is None:
|
|
451
|
+
call_stack = []
|
|
452
|
+
|
|
440
453
|
# Check if the step is relevant for testable_paths extraction.
|
|
441
|
-
# We only create new path nodes for
|
|
442
|
-
#
|
|
443
|
-
#
|
|
454
|
+
# We only create new path nodes for CollectInformationFlowStep,
|
|
455
|
+
# ActionFlowStep, CallFlowStep and LinkFlowStep,
|
|
456
|
+
# because these are externally visible changes
|
|
457
|
+
# in the assistant's behaviour (trackable in the e2e tests).
|
|
444
458
|
# For other flow steps, we only follow their links.
|
|
445
|
-
# We decided to ignore calls to other flows in our coverage analysis.
|
|
446
459
|
should_add_node = isinstance(
|
|
447
|
-
current_step,
|
|
460
|
+
current_step,
|
|
461
|
+
(CollectInformationFlowStep, ActionFlowStep, CallFlowStep, LinkFlowStep),
|
|
448
462
|
)
|
|
449
463
|
if should_add_node:
|
|
450
464
|
# Add current step to the current path that is being constructed.
|
|
@@ -456,10 +470,45 @@ class Flow:
|
|
|
456
470
|
)
|
|
457
471
|
)
|
|
458
472
|
|
|
473
|
+
# Check if the current step has already been visited or
|
|
474
|
+
# if the end of the path has been reached.
|
|
475
|
+
# If so, and we’re not within a called flow, we terminate the current path.
|
|
476
|
+
# This also applies for when we're inside a linked flow and reach its end.
|
|
477
|
+
# If we're inside a called flow and reach its end,
|
|
478
|
+
# continue with the next steps in its parent flow.
|
|
459
479
|
if current_step.id in visited_step_ids or self.is_end_of_path(current_step):
|
|
460
|
-
#
|
|
461
|
-
|
|
462
|
-
#
|
|
480
|
+
# Shallow copy is sufficient, since we only pop from the list and
|
|
481
|
+
# don't mutate the objects inside the tuples.
|
|
482
|
+
# The state of FlowStep and Flow does not change during the traversal.
|
|
483
|
+
call_stack_copy = call_stack.copy()
|
|
484
|
+
# parent_flow_type could be any of: None, i.e. main flow,
|
|
485
|
+
# KEY_CALLED_FLOW(=called_flow) or KEY_LINKED_FLOW(=linked_flow)
|
|
486
|
+
parent_step, parent_flow, parent_flow_type = (
|
|
487
|
+
call_stack_copy.pop() if call_stack_copy else (None, None, None)
|
|
488
|
+
)
|
|
489
|
+
|
|
490
|
+
# Check if within a called flow.
|
|
491
|
+
# If within linked flow, stop the traversal as this takes precedence.
|
|
492
|
+
if parent_step and parent_flow_type == KEY_CALLED_FLOW:
|
|
493
|
+
# As we have reached the END step of a called flow, we need to
|
|
494
|
+
# continue with the next links of the parent step.
|
|
495
|
+
if parent_flow is not None:
|
|
496
|
+
for link in parent_step.next.links:
|
|
497
|
+
parent_flow._handle_link(
|
|
498
|
+
current_path,
|
|
499
|
+
all_paths,
|
|
500
|
+
visited_step_ids,
|
|
501
|
+
link,
|
|
502
|
+
call_stack_copy,
|
|
503
|
+
)
|
|
504
|
+
|
|
505
|
+
else:
|
|
506
|
+
# Found a cycle, or reached an end step, do not proceed further.
|
|
507
|
+
all_paths.paths.append(copy.deepcopy(current_path))
|
|
508
|
+
|
|
509
|
+
# Backtrack: remove the last node after reaching a terminal step.
|
|
510
|
+
# Ensures the path is correctly backtracked, after a path ends or
|
|
511
|
+
# a cycle is detected.
|
|
463
512
|
if should_add_node:
|
|
464
513
|
current_path.nodes.pop()
|
|
465
514
|
return
|
|
@@ -467,6 +516,62 @@ class Flow:
|
|
|
467
516
|
# Mark current step as visited in this path.
|
|
468
517
|
visited_step_ids.add(current_step.id)
|
|
469
518
|
|
|
519
|
+
# If the current step is a call step, we need to resolve the call
|
|
520
|
+
# and continue with the steps of the called flow.
|
|
521
|
+
if isinstance(current_step, CallFlowStep):
|
|
522
|
+
# Get the steps of the called flow and continue with them.
|
|
523
|
+
called_flow = current_step.called_flow_reference
|
|
524
|
+
if called_flow and (
|
|
525
|
+
start_step_in_called_flow := called_flow.first_step_in_flow()
|
|
526
|
+
):
|
|
527
|
+
call_stack.append((current_step, self, KEY_CALLED_FLOW))
|
|
528
|
+
called_flow._go_over_steps(
|
|
529
|
+
start_step_in_called_flow,
|
|
530
|
+
current_path,
|
|
531
|
+
all_paths,
|
|
532
|
+
visited_step_ids,
|
|
533
|
+
call_stack,
|
|
534
|
+
)
|
|
535
|
+
|
|
536
|
+
# After processing the steps of the called (child) flow,
|
|
537
|
+
# remove them from the visited steps
|
|
538
|
+
# to allow the calling (parent) flow to revisit them later.
|
|
539
|
+
visited_step_ids.remove(current_step.id)
|
|
540
|
+
call_stack.pop()
|
|
541
|
+
|
|
542
|
+
# Backtrack: remove the last node
|
|
543
|
+
# after returning from a called (child) flow.
|
|
544
|
+
# Ensures the parent flow can continue exploring other branches.
|
|
545
|
+
if should_add_node:
|
|
546
|
+
current_path.nodes.pop()
|
|
547
|
+
return
|
|
548
|
+
|
|
549
|
+
# If the current step is a LinkFlowStep, step into the linked flow,
|
|
550
|
+
# process its links, and do not return from that flow anymore.
|
|
551
|
+
if isinstance(current_step, LinkFlowStep):
|
|
552
|
+
# Get the steps of the linked flow and continue with them.
|
|
553
|
+
linked_flow = current_step.linked_flow_reference
|
|
554
|
+
if linked_flow and (
|
|
555
|
+
start_step_in_linked_flow := linked_flow.first_step_in_flow()
|
|
556
|
+
):
|
|
557
|
+
call_stack.append((current_step, self, KEY_LINKED_FLOW))
|
|
558
|
+
linked_flow._go_over_steps(
|
|
559
|
+
start_step_in_linked_flow,
|
|
560
|
+
current_path,
|
|
561
|
+
all_paths,
|
|
562
|
+
visited_step_ids,
|
|
563
|
+
call_stack,
|
|
564
|
+
)
|
|
565
|
+
visited_step_ids.remove(current_step.id)
|
|
566
|
+
call_stack.pop()
|
|
567
|
+
|
|
568
|
+
# Backtrack: remove the last node
|
|
569
|
+
# after returning from a linked (child) flow.
|
|
570
|
+
# Ensures the parent can continue after the linked flow is processed.
|
|
571
|
+
if should_add_node:
|
|
572
|
+
current_path.nodes.pop()
|
|
573
|
+
return
|
|
574
|
+
|
|
470
575
|
# Iterate over all links of the current step.
|
|
471
576
|
for link in current_step.next.links:
|
|
472
577
|
self._handle_link(
|
|
@@ -474,12 +579,15 @@ class Flow:
|
|
|
474
579
|
all_paths,
|
|
475
580
|
visited_step_ids,
|
|
476
581
|
link,
|
|
582
|
+
call_stack,
|
|
477
583
|
)
|
|
478
584
|
|
|
479
585
|
# Backtrack the current step and remove it from the path.
|
|
480
586
|
visited_step_ids.remove(current_step.id)
|
|
481
587
|
|
|
482
|
-
#
|
|
588
|
+
# Backtrack: remove the last node
|
|
589
|
+
# after processing all links of the current step.
|
|
590
|
+
# Ensures the next recursion can start once all links are explored.
|
|
483
591
|
if should_add_node:
|
|
484
592
|
current_path.nodes.pop()
|
|
485
593
|
|
|
@@ -489,6 +597,9 @@ class Flow:
|
|
|
489
597
|
all_paths: FlowPathsList,
|
|
490
598
|
visited_step_ids: Set[str],
|
|
491
599
|
link: FlowStepLink,
|
|
600
|
+
call_stack: Optional[
|
|
601
|
+
List[Tuple[Optional[FlowStep], Optional[Flow], str]]
|
|
602
|
+
] = None,
|
|
492
603
|
) -> None:
|
|
493
604
|
"""Handles the next step in a flow.
|
|
494
605
|
|
|
@@ -497,6 +608,8 @@ class Flow:
|
|
|
497
608
|
all_paths: The list where completed paths are added.
|
|
498
609
|
visited_step_ids: A set of steps that have been visited to avoid cycles.
|
|
499
610
|
link: The link to be followed.
|
|
611
|
+
call_stack: Tuple list of (flow, path, flow_type) to track path when \
|
|
612
|
+
calling flows through call and link steps..
|
|
500
613
|
|
|
501
614
|
Returns:
|
|
502
615
|
None: This function modifies all_paths in place by appending new paths
|
|
@@ -511,6 +624,7 @@ class Flow:
|
|
|
511
624
|
current_path,
|
|
512
625
|
all_paths,
|
|
513
626
|
visited_step_ids,
|
|
627
|
+
call_stack,
|
|
514
628
|
)
|
|
515
629
|
return
|
|
516
630
|
# IfFlowStepLink and ElseFlowStepLink are conditional links.
|
|
@@ -524,6 +638,7 @@ class Flow:
|
|
|
524
638
|
current_path,
|
|
525
639
|
all_paths,
|
|
526
640
|
visited_step_ids,
|
|
641
|
+
call_stack,
|
|
527
642
|
)
|
|
528
643
|
return
|
|
529
644
|
else:
|
|
@@ -534,6 +649,7 @@ class Flow:
|
|
|
534
649
|
current_path,
|
|
535
650
|
all_paths,
|
|
536
651
|
visited_step_ids,
|
|
652
|
+
call_stack,
|
|
537
653
|
)
|
|
538
654
|
return
|
|
539
655
|
|
|
@@ -36,6 +36,7 @@ class FlowsList:
|
|
|
36
36
|
def __post_init__(self) -> None:
|
|
37
37
|
"""Initializes the FlowsList object."""
|
|
38
38
|
self._resolve_called_flows()
|
|
39
|
+
self._resolve_linked_flows()
|
|
39
40
|
|
|
40
41
|
def __iter__(self) -> Generator[Flow, None, None]:
|
|
41
42
|
"""Iterates over the flows."""
|
|
@@ -103,7 +104,10 @@ class FlowsList:
|
|
|
103
104
|
)
|
|
104
105
|
|
|
105
106
|
def _resolve_called_flows(self) -> None:
|
|
106
|
-
"""Resolves the called flows.
|
|
107
|
+
"""Resolves the called flows.
|
|
108
|
+
|
|
109
|
+
`Resolving` here means connecting the step to the actual `Flow` object.
|
|
110
|
+
"""
|
|
107
111
|
from rasa.shared.core.flows.steps import CallFlowStep
|
|
108
112
|
|
|
109
113
|
for flow in self.underlying_flows:
|
|
@@ -112,6 +116,19 @@ class FlowsList:
|
|
|
112
116
|
# only resolve the reference, if it isn't already resolved
|
|
113
117
|
step.called_flow_reference = self.flow_by_id(step.call)
|
|
114
118
|
|
|
119
|
+
def _resolve_linked_flows(self) -> None:
|
|
120
|
+
"""Resolves the linked flows.
|
|
121
|
+
|
|
122
|
+
`Resolving` here means connecting the step to the actual `Flow` object.
|
|
123
|
+
"""
|
|
124
|
+
from rasa.shared.core.flows.steps import LinkFlowStep
|
|
125
|
+
|
|
126
|
+
for flow in self.underlying_flows:
|
|
127
|
+
for step in flow.steps:
|
|
128
|
+
if isinstance(step, LinkFlowStep) and not step.linked_flow_reference:
|
|
129
|
+
# only resolve the reference, if it isn't already resolved
|
|
130
|
+
step.linked_flow_reference = self.flow_by_id(step.link)
|
|
131
|
+
|
|
115
132
|
def as_json_list(self) -> List[Dict[Text, Any]]:
|
|
116
133
|
"""Serialize the FlowsList object to list format and not to the original dict.
|
|
117
134
|
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from dataclasses import dataclass
|
|
4
|
-
from typing import
|
|
4
|
+
from typing import TYPE_CHECKING, Any, Dict, Text
|
|
5
5
|
|
|
6
|
-
from rasa.shared.core.flows.flow_step import FlowStep
|
|
6
|
+
from rasa.shared.core.flows.flow_step import FlowStep, Optional
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from rasa.shared.core.flows.flow import Flow
|
|
7
10
|
|
|
8
11
|
|
|
9
12
|
@dataclass
|
|
@@ -12,6 +15,8 @@ class LinkFlowStep(FlowStep):
|
|
|
12
15
|
|
|
13
16
|
link: Text
|
|
14
17
|
"""The id of the flow that should be started subsequently."""
|
|
18
|
+
linked_flow_reference: Optional["Flow"] = None
|
|
19
|
+
"""The flow that is linked to by this step."""
|
|
15
20
|
|
|
16
21
|
def does_allow_for_next_step(self) -> bool:
|
|
17
22
|
"""Returns whether this step allows for following steps.
|
rasa/version.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: rasa-pro
|
|
3
|
-
Version: 3.11.
|
|
3
|
+
Version: 3.11.17
|
|
4
4
|
Summary: State-of-the-art open-core Conversational AI framework for Enterprises that natively leverages generative AI for effortless assistant development.
|
|
5
5
|
Keywords: nlp,machine-learning,machine-learning-library,bot,bots,botkit,rasa conversational-agents,conversational-ai,chatbot,chatbot-framework,bot-framework
|
|
6
6
|
Author: Rasa Technologies GmbH
|
|
@@ -321,7 +321,7 @@ rasa/core/nlg/summarize.py,sha256=0Was54hj7hCOwXPne_QXLyMkRyBn2ngQBE-Vl7TZ8GA,31
|
|
|
321
321
|
rasa/core/persistor.py,sha256=Rooda9cO9XZylLWFz2n3M2NAmiRbIK84pS50Fz47f7A,21295
|
|
322
322
|
rasa/core/policies/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
323
323
|
rasa/core/policies/ensemble.py,sha256=AjNOEy2Iubbe-LdKaoFUXG8ch6yPrg3bTvcTcAPmeOs,12959
|
|
324
|
-
rasa/core/policies/enterprise_search_policy.py,sha256=
|
|
324
|
+
rasa/core/policies/enterprise_search_policy.py,sha256=WT5D9tw3-6c70ky7mdr2LQ5G4M-LQFVhCydGCmobeWs,39075
|
|
325
325
|
rasa/core/policies/enterprise_search_prompt_template.jinja2,sha256=dCS_seyBGxMQoMsOjjvPp0dd31OSzZCJSZeev1FJK5Q,1187
|
|
326
326
|
rasa/core/policies/enterprise_search_prompt_with_citation_template.jinja2,sha256=va9rpP97dN3PKoJZOVfyuISt3cPBlb10Pqyz25RwO_Q,3294
|
|
327
327
|
rasa/core/policies/flow_policy.py,sha256=wGb1l_59cGM9ZaexSIK5uXFi618739oNfLOxx2FC0_Y,7490
|
|
@@ -336,7 +336,7 @@ rasa/core/policies/policy.py,sha256=HeVtIaV0dA1QcAG3vjdn-4g7-oUEJPL4u01ETJt78YA,
|
|
|
336
336
|
rasa/core/policies/rule_policy.py,sha256=YNDPZUZkpKFCvZwKe1kSfP6LQnDL9CQ6JU69JRwdmWw,50729
|
|
337
337
|
rasa/core/policies/ted_policy.py,sha256=_DHiDH5Upx1yFNzMXBA3SGdHBRfsitTLlr7howUHPoo,87750
|
|
338
338
|
rasa/core/policies/unexpected_intent_policy.py,sha256=5pGe9EMS-NLHIDDhqY6KCH_Kv7_TGMzSbe_GsjuKH1w,39649
|
|
339
|
-
rasa/core/processor.py,sha256=
|
|
339
|
+
rasa/core/processor.py,sha256=Dbr40giQRL8k039CPC9DMKMxqu8Ue6cqNskLMjMwenY,57221
|
|
340
340
|
rasa/core/run.py,sha256=NxZ2iZm_nYhaxEN7jvWSVxMAMqZTHj0m2Y7dqCxb3Pw,11653
|
|
341
341
|
rasa/core/secrets_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
342
342
|
rasa/core/secrets_manager/constants.py,sha256=dTDHenvG1JBVi34QIR6FpdO5RDOXQwAjAxLlgJ2ZNEI,1193
|
|
@@ -363,7 +363,7 @@ rasa/dialogue_understanding/coexistence/llm_based_router.py,sha256=7qLFOYm6MSRNJ
|
|
|
363
363
|
rasa/dialogue_understanding/coexistence/router_template.jinja2,sha256=CHWFreN0sv1EbPh-hf5AlCt3zxy2_llX1Pdn9Q11Y18,357
|
|
364
364
|
rasa/dialogue_understanding/commands/__init__.py,sha256=hzlP15s6Qpr5acSWbS2svWvCNfY7GY2HFqtpVUhHjx4,2259
|
|
365
365
|
rasa/dialogue_understanding/commands/can_not_handle_command.py,sha256=2sNnIo1jZ2UEadkBdEWmDasm2tBES59lzvFcf0iY51U,2235
|
|
366
|
-
rasa/dialogue_understanding/commands/cancel_flow_command.py,sha256=
|
|
366
|
+
rasa/dialogue_understanding/commands/cancel_flow_command.py,sha256=FkAOPTOU8Zn1Ph5c0I6U8mzTvxtWBHfildm6h3-zaGQ,4331
|
|
367
367
|
rasa/dialogue_understanding/commands/change_flow_command.py,sha256=0s3g-3InNZs2eAiI7kmBKp3MzBJN69YzdT-IFJjaCaE,1338
|
|
368
368
|
rasa/dialogue_understanding/commands/chit_chat_answer_command.py,sha256=jl8em1Xw867VAj5EogeuOjWK93uoT71IhjHU076c2mg,1773
|
|
369
369
|
rasa/dialogue_understanding/commands/clarify_command.py,sha256=OzkqVRZrQo20cveqUoQGZE96DdRLLPgfPxJhroubIQw,2822
|
|
@@ -375,15 +375,16 @@ rasa/dialogue_understanding/commands/handle_code_change_command.py,sha256=pKaj8E
|
|
|
375
375
|
rasa/dialogue_understanding/commands/human_handoff_command.py,sha256=viSzL3Zmudm2WEjWmOS2s0zfOTXNsWoVU2pS-JXDFlU,1928
|
|
376
376
|
rasa/dialogue_understanding/commands/knowledge_answer_command.py,sha256=JPiWQC5qWJmryE7DgApDlf9AdGVMazuU9TXx44gc78w,1773
|
|
377
377
|
rasa/dialogue_understanding/commands/noop_command.py,sha256=302wi-pPXnraqclTDXug6_IYucEZCwDtP032OhPv1JY,1476
|
|
378
|
+
rasa/dialogue_understanding/commands/pattern_to_command_mapping.py,sha256=OiyLFGEsrfFSIJcvBY6lTIIXqDY9OxaikVGtcl4Kokk,1911
|
|
378
379
|
rasa/dialogue_understanding/commands/repeat_bot_messages_command.py,sha256=9hbDrTruhe2NogR8Xtt0SVcJUAlN2sQTzmynCaB_-ns,1858
|
|
379
380
|
rasa/dialogue_understanding/commands/restart_command.py,sha256=lUGO8iAyHH6esuWi7sKZQzZIfzYipx4Vo58-B9U20gg,1678
|
|
380
381
|
rasa/dialogue_understanding/commands/session_end_command.py,sha256=xMVOZJv7J3WAShGzJywQPcd5w263w6K_eYSROwpHUsk,1808
|
|
381
382
|
rasa/dialogue_understanding/commands/session_start_command.py,sha256=k5cGcE4Usxwfua1sE-KY-qWdwHlHZKgVx5XPDt_-RFY,1742
|
|
382
|
-
rasa/dialogue_understanding/commands/set_slot_command.py,sha256=
|
|
383
|
+
rasa/dialogue_understanding/commands/set_slot_command.py,sha256=7HLw7iSXxygLG5v1ppV-iCkEDfg4ODvSIqV30rmW3jo,5537
|
|
383
384
|
rasa/dialogue_understanding/commands/skip_question_command.py,sha256=bSrUFOHUz1ozdaHma-KAaAArhP59RB3W8CEEBQaPIkA,2251
|
|
384
385
|
rasa/dialogue_understanding/commands/start_flow_command.py,sha256=a0Yk8xpBpFgC3Hkh4J8kAudz4s4ZLQWuoDq_a63lQXM,3309
|
|
385
386
|
rasa/dialogue_understanding/commands/user_silence_command.py,sha256=QtqsMU5mrbUp5dla2yGSpxXfIfi_h6Eu72mTDZQ_aTU,1724
|
|
386
|
-
rasa/dialogue_understanding/commands/utils.py,sha256=
|
|
387
|
+
rasa/dialogue_understanding/commands/utils.py,sha256=GA3J3K2fLhMJNtbul8Es9UdkIAmm8zT2MP9GBM9gITI,766
|
|
387
388
|
rasa/dialogue_understanding/generator/__init__.py,sha256=Ykeb2wQ1DuiUWAWO0hLIPSTK1_Ktiq9DZXF6D3ugN78,764
|
|
388
389
|
rasa/dialogue_understanding/generator/command_generator.py,sha256=woJ4-59Iarugyxy0fT0zM0QnF8el8m2bBSpy-9Gh8U0,11945
|
|
389
390
|
rasa/dialogue_understanding/generator/constants.py,sha256=KhvcY7rP8x3oWOSqFqE6JYoHzfYIqDFT-o4KbrDUPTY,811
|
|
@@ -395,7 +396,7 @@ rasa/dialogue_understanding/generator/multi_step/__init__.py,sha256=47DEQpj8HBSa
|
|
|
395
396
|
rasa/dialogue_understanding/generator/multi_step/fill_slots_prompt.jinja2,sha256=Y0m673tAML3cFPaLM-urMXDsBYUUcXIw9YUpkAhGUuA,2933
|
|
396
397
|
rasa/dialogue_understanding/generator/multi_step/handle_flows_prompt.jinja2,sha256=8l93_QBKBYnqLICVdiTu5ejZDE8F36BU8-qwba0px44,1927
|
|
397
398
|
rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py,sha256=jo3hZO2SYDgUdwYGKAqiJJC2uYTwyBrOQo9o6u1mq0c,32670
|
|
398
|
-
rasa/dialogue_understanding/generator/nlu_command_adapter.py,sha256=
|
|
399
|
+
rasa/dialogue_understanding/generator/nlu_command_adapter.py,sha256=AcmzRZZM0x3_mauIverkbKRykg7xHSh_dggVqlAVD6c,9231
|
|
399
400
|
rasa/dialogue_understanding/generator/single_step/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
400
401
|
rasa/dialogue_understanding/generator/single_step/command_prompt_template.jinja2,sha256=nMayu-heJYH1QmcL1cFmXb8SeiJzfdDR_9Oy5IRUXsM,3937
|
|
401
402
|
rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py,sha256=UqbbuFCPaNHVL6Q-8XB_bMWYbQ07awlRPjH7nIoMatk,18170
|
|
@@ -419,17 +420,17 @@ rasa/dialogue_understanding/patterns/session_start.py,sha256=yglhIEkkquRf0YppZ4C
|
|
|
419
420
|
rasa/dialogue_understanding/patterns/skip_question.py,sha256=rvZuVUxulikwUhP01MAIgkcHZ4Si7mzxNedH6QBPdX4,1214
|
|
420
421
|
rasa/dialogue_understanding/patterns/user_silence.py,sha256=uwpCJRkRmeSvFDZQBZnEL4rumweF6mQ8ht_WqrTPVKU,1140
|
|
421
422
|
rasa/dialogue_understanding/processor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
422
|
-
rasa/dialogue_understanding/processor/command_processor.py,sha256=
|
|
423
|
+
rasa/dialogue_understanding/processor/command_processor.py,sha256=Qor-Dfnnku-9xCgglUYuPrPn85Saj37xK5lUNAZ9t0I,26088
|
|
423
424
|
rasa/dialogue_understanding/processor/command_processor_component.py,sha256=rkErI_Uo7s3LsEojUSGSRbWGyGaX7GtGOYSJn0V-TI4,1650
|
|
424
425
|
rasa/dialogue_understanding/stack/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
425
426
|
rasa/dialogue_understanding/stack/dialogue_stack.py,sha256=j8MnLCyv6cAZVpKRaUVM-Z5HqgWP-scrnaiQXzLNBwY,5243
|
|
426
427
|
rasa/dialogue_understanding/stack/frames/__init__.py,sha256=CXLs8I_eeJ-d2tQmS19V26OM6CHy3VN5whH5uHBodj4,656
|
|
427
428
|
rasa/dialogue_understanding/stack/frames/chit_chat_frame.py,sha256=bfMCyeTK69SB3KP9TUCfdBGNDmNbblfDNWM_ll3ppPc,736
|
|
428
429
|
rasa/dialogue_understanding/stack/frames/dialogue_stack_frame.py,sha256=rmG09a66PerYiuoWiG-Jlb4Oi6bct1OIHAJGTMN_F2M,4126
|
|
429
|
-
rasa/dialogue_understanding/stack/frames/flow_stack_frame.py,sha256=
|
|
430
|
+
rasa/dialogue_understanding/stack/frames/flow_stack_frame.py,sha256=HRlR5YCkoOlI1DWCkgl_ak3YjFQEbmgwvzC16elkK54,5578
|
|
430
431
|
rasa/dialogue_understanding/stack/frames/pattern_frame.py,sha256=EVrYWv5dCP7XTvNV-HqtOOrseP-IkF0jD2_JacAvIYw,235
|
|
431
432
|
rasa/dialogue_understanding/stack/frames/search_frame.py,sha256=rJ9og28k_udUIjP-2Z5xeb_2T5HvCzwDCnxVG9K7lws,728
|
|
432
|
-
rasa/dialogue_understanding/stack/utils.py,sha256=
|
|
433
|
+
rasa/dialogue_understanding/stack/utils.py,sha256=PWA4b1EgRGVZx5uWWa1ZVM45WhJ5tr0C9Md5TA7pxpU,8019
|
|
433
434
|
rasa/e2e_test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
434
435
|
rasa/e2e_test/aggregate_test_stats_calculator.py,sha256=XMI7t5xEP7Mo-F8cCCZx2w5ckUKa5sDvyitl6bk6hc8,4924
|
|
435
436
|
rasa/e2e_test/assertions.py,sha256=ofn2CbnW2wR3OZ3-PPmD_07YqlGQ_tWL1TSv95vxKy0,45428
|
|
@@ -440,7 +441,7 @@ rasa/e2e_test/e2e_config_schema.yml,sha256=V5wmuMOCJDrXUdNHdG7B_5JhFFt8PmXhHh0cB
|
|
|
440
441
|
rasa/e2e_test/e2e_test_case.py,sha256=MkKCMD-6oPqytBlPyaD8an9mIsP2wZZz_0lTyUCjIIo,20714
|
|
441
442
|
rasa/e2e_test/e2e_test_converter.py,sha256=VxIx7uk36HzLIyEumJiR6G6-CyyqkV_lYoX-XQ9eKno,12588
|
|
442
443
|
rasa/e2e_test/e2e_test_converter_prompt.jinja2,sha256=EMy-aCd7jLARHmwAuZUGT5ABnNHjR872_pexRIMGA7c,2791
|
|
443
|
-
rasa/e2e_test/e2e_test_coverage_report.py,sha256=
|
|
444
|
+
rasa/e2e_test/e2e_test_coverage_report.py,sha256=rEoEiyMOuxFxNSzpXQO6NiCACKeGQXuh1hGh4WfMaJE,11344
|
|
444
445
|
rasa/e2e_test/e2e_test_result.py,sha256=9LlH6vIQeK_dxDwMQ5RzlNHoxCNXpWC9S527-ch8kUA,1649
|
|
445
446
|
rasa/e2e_test/e2e_test_runner.py,sha256=IbQKWXQUmBJDnb3v1TLOjH6FXD0ADCL-IVneUsZcN8I,44146
|
|
446
447
|
rasa/e2e_test/e2e_test_schema.yml,sha256=0deWjuKRHNo6e_LSCnUoiw9NLIYf6dj1-zFPl_AqLYA,5632
|
|
@@ -595,12 +596,13 @@ rasa/shared/core/conversation.py,sha256=tw1fD2XB3gOdQjDI8hHo5TAAmE2JYNogQGWe3rE9
|
|
|
595
596
|
rasa/shared/core/domain.py,sha256=bGjonMV54wbwGLPjKHI2NoWwxr2wyUZwhEvjBWhP-W0,81710
|
|
596
597
|
rasa/shared/core/events.py,sha256=zdGSP1bNV1RyKC9Z54S7EbQ8TfGne_n9XKj64aoghdI,85803
|
|
597
598
|
rasa/shared/core/flows/__init__.py,sha256=HszhIvEARpmyxABFc1MKYvj8oy04WiZW1xmCdToakbs,181
|
|
598
|
-
rasa/shared/core/flows/
|
|
599
|
+
rasa/shared/core/flows/constants.py,sha256=oLzPFeBIBTfV7Jh55rXXOKiep9jEEfzES_THjUrIxDM,64
|
|
600
|
+
rasa/shared/core/flows/flow.py,sha256=sXD8EdRlwCK6l7t-LXX_qn18XqpNunG8U-zwDia1Fy8,26083
|
|
599
601
|
rasa/shared/core/flows/flow_path.py,sha256=xstwahZBU5cfMY46mREA4NoOGlKLBRAqeP_mJ3UZqOI,2283
|
|
600
602
|
rasa/shared/core/flows/flow_step.py,sha256=6hoVOMXryTKHgT7-p7jzTqH2r9QREmy_6d6bX2OyxI0,4550
|
|
601
603
|
rasa/shared/core/flows/flow_step_links.py,sha256=zMZV_9rWVjEa8kHIFSIbXCWA1qaUvp8r4uSCK_NsKKs,10548
|
|
602
604
|
rasa/shared/core/flows/flow_step_sequence.py,sha256=tXUEhubX3g312G2FH8Hiez7_v9XG9EaspKlANRa7Sa8,2362
|
|
603
|
-
rasa/shared/core/flows/flows_list.py,sha256
|
|
605
|
+
rasa/shared/core/flows/flows_list.py,sha256=LLkiPEdO0QqlXQ7aVVZkYNzA5hgX5BJGdIS542-5jsk,9415
|
|
604
606
|
rasa/shared/core/flows/flows_yaml_schema.json,sha256=9LjPQ_xtU8n4zvcxmeolGRT2S0_1tIngdZsncAmVK78,11189
|
|
605
607
|
rasa/shared/core/flows/nlu_trigger.py,sha256=gfnRUi7QJaUjMb43RTyQWbLoIUSx2I78LeM8CjDIIDE,3907
|
|
606
608
|
rasa/shared/core/flows/steps/__init__.py,sha256=M43kHDuB8okcIE3WLivnvuCBzIsA5Qi2ATXUGTVW2Rw,655
|
|
@@ -611,7 +613,7 @@ rasa/shared/core/flows/steps/constants.py,sha256=DCxrEUGbJciBknHm-_t4tmcnH19IZKP
|
|
|
611
613
|
rasa/shared/core/flows/steps/continuation.py,sha256=5Rzayr80FsgS4bAajuRObVvVcLqPEh9nxGbT2te85xY,1498
|
|
612
614
|
rasa/shared/core/flows/steps/end.py,sha256=0XrPlQMVBnQKVeZs0or8P9IrVqG7i6RoSNDsVrvAeDk,749
|
|
613
615
|
rasa/shared/core/flows/steps/internal.py,sha256=vqBapuLBYQin3Me-YZKn_Tyg5VKozRIJ-tFmjVvmFBs,1449
|
|
614
|
-
rasa/shared/core/flows/steps/link.py,sha256=
|
|
616
|
+
rasa/shared/core/flows/steps/link.py,sha256=nOY1a92OEDjf4rqoZlbGAW2lXMeGOyNZwgLIK6e7if4,1702
|
|
615
617
|
rasa/shared/core/flows/steps/no_operation.py,sha256=I0xWFWJqhUAbvU0HSFXtw4v2gmIiTEveGv4AImvLDfo,1399
|
|
616
618
|
rasa/shared/core/flows/steps/set_slots.py,sha256=NgBXLPl5adIa0-M-Tgt2Teg3gXYggcDqpaf96BPpvKE,1492
|
|
617
619
|
rasa/shared/core/flows/steps/start.py,sha256=AJpKIm0S3GZYLEs3ybXW0Zrq03Pu9lvirNahiUy2I6k,1010
|
|
@@ -780,9 +782,9 @@ rasa/utils/train_utils.py,sha256=f1NWpp5y6al0dzoQyyio4hc4Nf73DRoRSHDzEK6-C4E,212
|
|
|
780
782
|
rasa/utils/url_tools.py,sha256=JQcHL2aLqLHu82k7_d9imUoETCm2bmlHaDpOJ-dKqBc,1218
|
|
781
783
|
rasa/utils/yaml.py,sha256=KjbZq5C94ZP7Jdsw8bYYF7HASI6K4-C_kdHfrnPLpSI,2000
|
|
782
784
|
rasa/validator.py,sha256=O1wjCeV7ITJ0luvb3GCWy8x1fGgzWVbClEMlPnLBowQ,67265
|
|
783
|
-
rasa/version.py,sha256
|
|
784
|
-
rasa_pro-3.11.
|
|
785
|
-
rasa_pro-3.11.
|
|
786
|
-
rasa_pro-3.11.
|
|
787
|
-
rasa_pro-3.11.
|
|
788
|
-
rasa_pro-3.11.
|
|
785
|
+
rasa/version.py,sha256=-sDWwO6NaEndrFCW_IM97xNLKz_ChNxABiOrABXSgj0,118
|
|
786
|
+
rasa_pro-3.11.17.dist-info/METADATA,sha256=H8kcNcNjldaFNlletjHpLVDi2MD4AVEru07HLJJeAto,10725
|
|
787
|
+
rasa_pro-3.11.17.dist-info/NOTICE,sha256=7HlBoMHJY9CL2GlYSfTQ-PZsVmLmVkYmMiPlTjhuCqA,218
|
|
788
|
+
rasa_pro-3.11.17.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
|
|
789
|
+
rasa_pro-3.11.17.dist-info/entry_points.txt,sha256=ckJ2SfEyTPgBqj_I6vm_tqY9dZF_LAPJZA335Xp0Q9U,43
|
|
790
|
+
rasa_pro-3.11.17.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|