rasa-pro 3.11.16__py3-none-any.whl → 3.11.18__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/channels/inspector/dist/assets/{arc-f09fea11.js → arc-6f1cf469.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{blockDiagram-38ab4fdb-95518007.js → blockDiagram-38ab4fdb-e4687a7a.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{c4Diagram-3d4e48cf-c91a4a08.js → c4Diagram-3d4e48cf-350db3c4.js} +1 -1
- rasa/core/channels/inspector/dist/assets/channel-e01523b7.js +1 -0
- rasa/core/channels/inspector/dist/assets/{classDiagram-70f12bd4-27f7869b.js → classDiagram-70f12bd4-87791d92.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{classDiagram-v2-f2320105-1ab94cdb.js → classDiagram-v2-f2320105-325a055a.js} +1 -1
- rasa/core/channels/inspector/dist/assets/clone-3bfaf7a0.js +1 -0
- rasa/core/channels/inspector/dist/assets/{createText-2e5e7dd3-a7900089.js → createText-2e5e7dd3-be863ae0.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{edges-e0da2a9e-3d5b2697.js → edges-e0da2a9e-ced68061.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{erDiagram-9861fffd-443cc11b.js → erDiagram-9861fffd-60684f40.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{flowDb-956e92f1-8a6f8c52.js → flowDb-956e92f1-664e800c.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{flowDiagram-66a62f08-06a0b4f3.js → flowDiagram-66a62f08-6ebc9040.js} +1 -1
- rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-ce3f0138.js +1 -0
- rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-4a651766-7a01e0b5.js → flowchart-elk-definition-4a651766-3a139e6d.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{ganttDiagram-c361ad54-5f1289f2.js → ganttDiagram-c361ad54-b4446b93.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-72cf32ee-44409666.js → gitGraphDiagram-72cf32ee-0ca05d79.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{graph-3c393c89.js → graph-d55c7150.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{index-3862675e-4d0c4142.js → index-3862675e-21ec23d0.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{index-b208b2c3.js → index-397b9bf3.js} +148 -148
- rasa/core/channels/inspector/dist/assets/{infoDiagram-f8f76790-ae0fa7ff.js → infoDiagram-f8f76790-1dbec276.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{journeyDiagram-49397b02-5c3b08cc.js → journeyDiagram-49397b02-c59ccb68.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{layout-b24c95cb.js → layout-084d6acb.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{line-999a77c5.js → line-7e3445bc.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{linear-81a792fd.js → linear-37acf20b.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{mindmap-definition-fc14e90a-c574f712.js → mindmap-definition-fc14e90a-053067ef.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{pieDiagram-8a3498a8-1919891d.js → pieDiagram-8a3498a8-00feec2e.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{quadrantDiagram-120e2f19-26e43d09.js → quadrantDiagram-120e2f19-7b0f9725.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{requirementDiagram-deff3bca-f4b22985.js → requirementDiagram-deff3bca-c23f6699.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{sankeyDiagram-04a897e0-b957b472.js → sankeyDiagram-04a897e0-7d7264d7.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{sequenceDiagram-704730f1-1d8ca073.js → sequenceDiagram-704730f1-78c3fce6.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{stateDiagram-587899a1-c67b1b71.js → stateDiagram-587899a1-4a241b87.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-d93cdb3a-ee820f55.js → stateDiagram-v2-d93cdb3a-5c57975b.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-6aaf32cf-b162bdf3.js → styles-6aaf32cf-af1e4802.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-9a916d00-67a7b254.js → styles-9a916d00-1a8391b1.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-c10674c1-81a8ac73.js → styles-c10674c1-bbd035f8.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{svgDrawCommon-08f97a94-ede42905.js → svgDrawCommon-08f97a94-e8aa007c.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{timeline-definition-85554ec2-b0f41635.js → timeline-definition-85554ec2-adc8097c.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{xychartDiagram-e933f94c-d715dfb0.js → xychartDiagram-e933f94c-3dd24cea.js} +1 -1
- rasa/core/channels/inspector/dist/index.html +1 -1
- rasa/core/channels/inspector/package.json +4 -3
- rasa/core/channels/inspector/yarn.lock +12 -12
- rasa/core/policies/enterprise_search_policy.py +196 -73
- rasa/dialogue_understanding/commands/cancel_flow_command.py +3 -2
- rasa/dialogue_understanding/commands/correct_slots_command.py +0 -10
- rasa/dialogue_understanding/processor/command_processor.py +23 -20
- 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.16.dist-info → rasa_pro-3.11.18.dist-info}/METADATA +4 -4
- {rasa_pro-3.11.16.dist-info → rasa_pro-3.11.18.dist-info}/RECORD +57 -56
- rasa/core/channels/inspector/dist/assets/channel-cc7720dc.js +0 -1
- rasa/core/channels/inspector/dist/assets/clone-3688e1f7.js +0 -1
- rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-5055ec2d.js +0 -1
- {rasa_pro-3.11.16.dist-info → rasa_pro-3.11.18.dist-info}/NOTICE +0 -0
- {rasa_pro-3.11.16.dist-info → rasa_pro-3.11.18.dist-info}/WHEEL +0 -0
- {rasa_pro-3.11.16.dist-info → rasa_pro-3.11.18.dist-info}/entry_points.txt +0 -0
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf"
|
|
8
8
|
integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==
|
|
9
9
|
|
|
10
|
-
"@adobe/css-tools@^4.3.1":
|
|
11
|
-
version "4.3
|
|
12
|
-
resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.3.
|
|
13
|
-
integrity sha512
|
|
10
|
+
"@adobe/css-tools@^4.3.1", "@adobe/css-tools@^4.3.2":
|
|
11
|
+
version "4.4.3"
|
|
12
|
+
resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.4.3.tgz#beebbefb0264fdeb32d3052acae0e0d94315a9a2"
|
|
13
|
+
integrity sha512-VQKMkwriZbaOgVCby1UDY/LDk5fIjhQicCvVPFqfe+69fWaPWydbWJ3wRt59/YzIwda1I81loas3oCoHxnqvdA==
|
|
14
14
|
|
|
15
15
|
"@ampproject/remapping@^2.2.0":
|
|
16
16
|
version "2.2.1"
|
|
@@ -2295,10 +2295,10 @@ available-typed-arrays@^1.0.5:
|
|
|
2295
2295
|
resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7"
|
|
2296
2296
|
integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==
|
|
2297
2297
|
|
|
2298
|
-
axios@1.
|
|
2299
|
-
version "1.
|
|
2300
|
-
resolved "https://registry.yarnpkg.com/axios/-/axios-1.
|
|
2301
|
-
integrity sha512-
|
|
2298
|
+
axios@1.8.2:
|
|
2299
|
+
version "1.8.2"
|
|
2300
|
+
resolved "https://registry.yarnpkg.com/axios/-/axios-1.8.2.tgz#fabe06e241dfe83071d4edfbcaa7b1c3a40f7979"
|
|
2301
|
+
integrity sha512-ls4GYBm5aig9vWx8AWDSGLpnpDQRtWAfrjU+EuytuODrFBkqesN2RkOQCBzrA1RQNHw1SmRMSDDDSwzNAYQ6Rg==
|
|
2302
2302
|
dependencies:
|
|
2303
2303
|
follow-redirects "^1.15.6"
|
|
2304
2304
|
form-data "^4.0.0"
|
|
@@ -6054,10 +6054,10 @@ v8-to-istanbul@^9.0.1:
|
|
|
6054
6054
|
"@types/istanbul-lib-coverage" "^2.0.1"
|
|
6055
6055
|
convert-source-map "^2.0.0"
|
|
6056
6056
|
|
|
6057
|
-
vite@4.5.
|
|
6058
|
-
version "4.5.
|
|
6059
|
-
resolved "https://registry.yarnpkg.com/vite/-/vite-4.5.
|
|
6060
|
-
integrity sha512-
|
|
6057
|
+
vite@4.5.12:
|
|
6058
|
+
version "4.5.12"
|
|
6059
|
+
resolved "https://registry.yarnpkg.com/vite/-/vite-4.5.12.tgz#48f48dbcf789722765e91bc32a99cb66c628eadc"
|
|
6060
|
+
integrity sha512-qrMwavANtSz91nDy3zEiUHMtL09x0mniQsSMvDkNxuCBM1W5vriJ22hEmwTth6DhLSWsZnHBT0yHFAQXt6efGA==
|
|
6061
6061
|
dependencies:
|
|
6062
6062
|
esbuild "^0.18.10"
|
|
6063
6063
|
postcss "^8.4.27"
|
|
@@ -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(
|
|
@@ -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:
|
|
@@ -225,16 +225,6 @@ class CorrectSlotsCommand(Command):
|
|
|
225
225
|
proposed_slots, all_flows, tracker
|
|
226
226
|
)
|
|
227
227
|
|
|
228
|
-
if not earliest_collect and not is_reset_only:
|
|
229
|
-
# if we could not find any step in the flow, where the slots were
|
|
230
|
-
# previously set, and we also don't want to reset the slots, do
|
|
231
|
-
# not correct the slots.
|
|
232
|
-
structlogger.debug(
|
|
233
|
-
"command_executor.skip_correction",
|
|
234
|
-
is_reset_only=is_reset_only,
|
|
235
|
-
)
|
|
236
|
-
return None
|
|
237
|
-
|
|
238
228
|
return CorrectionPatternFlowStackFrame(
|
|
239
229
|
is_reset_only=is_reset_only,
|
|
240
230
|
corrected_slots=proposed_slots,
|
|
@@ -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.
|
|
@@ -362,6 +356,11 @@ def clean_up_commands(
|
|
|
362
356
|
|
|
363
357
|
slots_so_far, active_flow = filled_slots_for_active_flow(tracker, all_flows)
|
|
364
358
|
|
|
359
|
+
# update the slots so far with the slots that were set in the tracker
|
|
360
|
+
slots_so_far.update(
|
|
361
|
+
{event.key for event in tracker.events if isinstance(event, SlotSet)}
|
|
362
|
+
)
|
|
363
|
+
|
|
365
364
|
clean_commands: List[Command] = []
|
|
366
365
|
|
|
367
366
|
for command in commands:
|
|
@@ -432,9 +431,9 @@ def clean_up_commands(
|
|
|
432
431
|
# when coexistence is enabled, by default there will be a SetSlotCommand
|
|
433
432
|
# for the ROUTE_TO_CALM_SLOT slot.
|
|
434
433
|
if tracker.has_coexistence_routing_slot and len(clean_commands) > 2:
|
|
435
|
-
clean_commands =
|
|
434
|
+
clean_commands = filter_cannot_handle_command(clean_commands)
|
|
436
435
|
elif not tracker.has_coexistence_routing_slot and len(clean_commands) > 1:
|
|
437
|
-
clean_commands =
|
|
436
|
+
clean_commands = filter_cannot_handle_command(clean_commands)
|
|
438
437
|
|
|
439
438
|
clean_commands = ensure_max_number_of_command_type(
|
|
440
439
|
clean_commands, RepeatBotMessagesCommand, 1
|
|
@@ -534,10 +533,18 @@ def clean_up_slot_command(
|
|
|
534
533
|
"command_processor.clean_up_slot_command.skip_command_slot_not_in_domain",
|
|
535
534
|
command=command,
|
|
536
535
|
)
|
|
536
|
+
resulting_commands.append(
|
|
537
|
+
CannotHandleCommand(
|
|
538
|
+
reason="The slot predicted by the LLM is not defined in the domain."
|
|
539
|
+
)
|
|
540
|
+
)
|
|
537
541
|
return resulting_commands
|
|
538
542
|
|
|
539
543
|
if not should_slot_be_set(slot, command):
|
|
540
|
-
cannot_handle = CannotHandleCommand(
|
|
544
|
+
cannot_handle = CannotHandleCommand(
|
|
545
|
+
reason="A command generator attempted to set a slot with a value extracted "
|
|
546
|
+
"by an extractor that is incompatible with the slot mapping type."
|
|
547
|
+
)
|
|
541
548
|
if cannot_handle not in resulting_commands:
|
|
542
549
|
resulting_commands.append(cannot_handle)
|
|
543
550
|
|
|
@@ -551,9 +558,9 @@ def clean_up_slot_command(
|
|
|
551
558
|
resulting_commands.append(command)
|
|
552
559
|
return resulting_commands
|
|
553
560
|
|
|
554
|
-
if (slot := tracker.slots.get(command.name)) is not None and
|
|
555
|
-
|
|
556
|
-
):
|
|
561
|
+
if (slot := tracker.slots.get(command.name)) is not None and str(
|
|
562
|
+
slot.value
|
|
563
|
+
) == str(command.value):
|
|
557
564
|
# the slot is already set, we don't need to set it again
|
|
558
565
|
structlogger.debug(
|
|
559
566
|
"command_processor.clean_up_slot_command.skip_command_slot_already_set",
|
|
@@ -713,12 +720,12 @@ def should_slot_be_set(slot: Slot, command: SetSlotCommand) -> bool:
|
|
|
713
720
|
return True
|
|
714
721
|
|
|
715
722
|
|
|
716
|
-
def
|
|
723
|
+
def filter_cannot_handle_command(
|
|
717
724
|
clean_commands: List[Command],
|
|
718
725
|
) -> List[Command]:
|
|
719
|
-
"""Filter out a 'cannot handle' command
|
|
726
|
+
"""Filter out a 'cannot handle' command.
|
|
720
727
|
|
|
721
|
-
This is used to filter out a 'cannot handle' command
|
|
728
|
+
This is used to filter out a 'cannot handle' command
|
|
722
729
|
in case other commands are present.
|
|
723
730
|
|
|
724
731
|
Returns:
|
|
@@ -727,9 +734,5 @@ def filter_cannot_handle_command_for_skipped_slots(
|
|
|
727
734
|
return [
|
|
728
735
|
command
|
|
729
736
|
for command in clean_commands
|
|
730
|
-
if not (
|
|
731
|
-
isinstance(command, CannotHandleCommand)
|
|
732
|
-
and command.reason
|
|
733
|
-
and CANNOT_HANDLE_REASON == command.reason
|
|
734
|
-
)
|
|
737
|
+
if not isinstance(command, CannotHandleCommand)
|
|
735
738
|
]
|
|
@@ -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"
|