rasa-pro 3.13.0.dev20250612__py3-none-any.whl → 3.13.0rc1__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/__main__.py +0 -3
- rasa/api.py +1 -1
- rasa/cli/dialogue_understanding_test.py +1 -1
- rasa/cli/e2e_test.py +1 -8
- rasa/cli/evaluate.py +1 -1
- rasa/cli/export.py +3 -1
- rasa/cli/llm_fine_tuning.py +12 -11
- rasa/cli/project_templates/defaults.py +133 -0
- rasa/cli/project_templates/tutorial/config.yml +1 -1
- rasa/cli/project_templates/tutorial/endpoints.yml +1 -1
- rasa/cli/run.py +1 -1
- rasa/cli/studio/download.py +1 -23
- rasa/cli/studio/link.py +52 -0
- rasa/cli/studio/pull.py +79 -0
- rasa/cli/studio/push.py +78 -0
- rasa/cli/studio/studio.py +12 -0
- rasa/cli/studio/train.py +0 -1
- rasa/cli/studio/upload.py +8 -0
- rasa/cli/train.py +1 -1
- rasa/cli/utils.py +1 -1
- rasa/cli/x.py +1 -1
- rasa/constants.py +2 -0
- rasa/core/__init__.py +0 -16
- rasa/core/actions/action.py +5 -1
- rasa/core/actions/action_repeat_bot_messages.py +18 -22
- rasa/core/actions/action_run_slot_rejections.py +0 -1
- rasa/core/agent.py +16 -1
- rasa/core/available_endpoints.py +146 -0
- rasa/core/brokers/pika.py +1 -2
- rasa/core/channels/__init__.py +2 -0
- rasa/core/channels/botframework.py +2 -2
- rasa/core/channels/channel.py +2 -2
- rasa/core/channels/development_inspector.py +1 -1
- rasa/core/channels/facebook.py +1 -4
- rasa/core/channels/hangouts.py +8 -5
- rasa/core/channels/inspector/README.md +3 -3
- rasa/core/channels/inspector/dist/assets/{arc-c4b064fc.js → arc-371401b1.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{blockDiagram-38ab4fdb-215b5026.js → blockDiagram-38ab4fdb-3f126156.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{c4Diagram-3d4e48cf-2b54a0a3.js → c4Diagram-3d4e48cf-12f22eb7.js} +1 -1
- rasa/core/channels/inspector/dist/assets/channel-f1efda17.js +1 -0
- rasa/core/channels/inspector/dist/assets/{classDiagram-70f12bd4-daacea5f.js → classDiagram-70f12bd4-03b1d386.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{classDiagram-v2-f2320105-930d4dc2.js → classDiagram-v2-f2320105-84f69d63.js} +1 -1
- rasa/core/channels/inspector/dist/assets/clone-fdf164e2.js +1 -0
- rasa/core/channels/inspector/dist/assets/{createText-2e5e7dd3-83c206ba.js → createText-2e5e7dd3-ca47fd38.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{edges-e0da2a9e-b0eb01d0.js → edges-e0da2a9e-f837ca8a.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{erDiagram-9861fffd-17586500.js → erDiagram-9861fffd-8717ac54.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{flowDb-956e92f1-be2a1776.js → flowDb-956e92f1-94f38b83.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{flowDiagram-66a62f08-c2120ebd.js → flowDiagram-66a62f08-b616f9fb.js} +1 -1
- rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-7d7a1629.js +1 -0
- rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-4a651766-a6ab5c48.js → flowchart-elk-definition-4a651766-f5d24bb8.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{ganttDiagram-c361ad54-ef613457.js → ganttDiagram-c361ad54-b43ba8d9.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-72cf32ee-d59185b3.js → gitGraphDiagram-72cf32ee-c3aafaa5.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{graph-0f155405.js → graph-0d0a2c10.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{index-3862675e-d5f1d1b7.js → index-3862675e-58ea0305.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{index-47737d3a.js → index-cce6f8a1.js} +3 -3
- rasa/core/channels/inspector/dist/assets/{infoDiagram-f8f76790-b07d141f.js → infoDiagram-f8f76790-b8f60461.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{journeyDiagram-49397b02-1936d429.js → journeyDiagram-49397b02-95be5545.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{layout-dde8d0f3.js → layout-da885b9b.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{line-0c2c7ee0.js → line-f1c817d3.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{linear-35dd89a4.js → linear-d42801e6.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{mindmap-definition-fc14e90a-56192851.js → mindmap-definition-fc14e90a-a38923a6.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{pieDiagram-8a3498a8-fc21ed78.js → pieDiagram-8a3498a8-ca6e71e9.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{quadrantDiagram-120e2f19-25e98518.js → quadrantDiagram-120e2f19-b290dae9.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{requirementDiagram-deff3bca-546ff1f5.js → requirementDiagram-deff3bca-03f02ceb.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{sankeyDiagram-04a897e0-02d8b82d.js → sankeyDiagram-04a897e0-c49eee40.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{sequenceDiagram-704730f1-3ca5a92e.js → sequenceDiagram-704730f1-b2cd6a3d.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{stateDiagram-587899a1-128ea07c.js → stateDiagram-587899a1-e53a2028.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-d93cdb3a-95f290af.js → stateDiagram-v2-d93cdb3a-e1982a03.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-6aaf32cf-4984898a.js → styles-6aaf32cf-d0226ca5.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-9a916d00-1bf266ba.js → styles-9a916d00-0e21dc00.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-c10674c1-60521c63.js → styles-c10674c1-9588494e.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{svgDrawCommon-08f97a94-a25b6e12.js → svgDrawCommon-08f97a94-be478d4f.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{timeline-definition-85554ec2-0fc086bf.js → timeline-definition-85554ec2-74631749.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{xychartDiagram-e933f94c-44ee592e.js → xychartDiagram-e933f94c-a043552f.js} +1 -1
- rasa/core/channels/inspector/dist/index.html +1 -1
- rasa/core/channels/inspector/src/components/RecruitmentPanel.tsx +1 -1
- rasa/core/channels/mattermost.py +1 -1
- rasa/core/channels/rasa_chat.py +2 -4
- rasa/core/channels/rest.py +5 -4
- rasa/core/channels/socketio.py +56 -41
- rasa/core/channels/studio_chat.py +314 -10
- rasa/core/channels/vier_cvg.py +1 -2
- rasa/core/channels/voice_ready/audiocodes.py +2 -9
- rasa/core/channels/voice_stream/asr/azure.py +9 -0
- rasa/core/channels/voice_stream/audiocodes.py +8 -5
- rasa/core/channels/voice_stream/browser_audio.py +1 -1
- rasa/core/channels/voice_stream/genesys.py +2 -2
- rasa/core/channels/voice_stream/jambonz.py +166 -0
- rasa/core/channels/voice_stream/tts/__init__.py +8 -0
- rasa/core/channels/voice_stream/twilio_media_streams.py +17 -5
- rasa/core/channels/voice_stream/voice_channel.py +44 -24
- rasa/core/exporter.py +36 -0
- rasa/core/http_interpreter.py +3 -7
- rasa/core/information_retrieval/faiss.py +18 -11
- rasa/core/information_retrieval/ingestion/faq_parser.py +158 -0
- rasa/core/jobs.py +2 -1
- rasa/core/nlg/contextual_response_rephraser.py +48 -12
- rasa/core/nlg/generator.py +0 -1
- rasa/core/nlg/interpolator.py +2 -3
- rasa/core/nlg/summarize.py +39 -5
- rasa/core/policies/enterprise_search_policy.py +298 -184
- rasa/core/policies/enterprise_search_policy_config.py +241 -0
- rasa/core/policies/enterprise_search_prompt_with_relevancy_check_and_citation_template.jinja2 +64 -0
- rasa/core/policies/flow_policy.py +1 -1
- rasa/core/policies/flows/flow_executor.py +96 -17
- rasa/core/policies/intentless_policy.py +71 -26
- rasa/core/processor.py +104 -51
- rasa/core/run.py +33 -11
- rasa/core/tracker_stores/tracker_store.py +1 -1
- rasa/core/training/interactive.py +1 -1
- rasa/core/utils.py +35 -99
- rasa/dialogue_understanding/coexistence/intent_based_router.py +2 -1
- rasa/dialogue_understanding/coexistence/llm_based_router.py +13 -17
- rasa/dialogue_understanding/commands/__init__.py +4 -0
- rasa/dialogue_understanding/commands/can_not_handle_command.py +2 -0
- rasa/dialogue_understanding/commands/cancel_flow_command.py +6 -2
- rasa/dialogue_understanding/commands/chit_chat_answer_command.py +2 -0
- rasa/dialogue_understanding/commands/clarify_command.py +7 -3
- rasa/dialogue_understanding/commands/command_syntax_manager.py +1 -0
- rasa/dialogue_understanding/commands/correct_slots_command.py +5 -6
- rasa/dialogue_understanding/commands/error_command.py +1 -1
- rasa/dialogue_understanding/commands/human_handoff_command.py +3 -3
- rasa/dialogue_understanding/commands/knowledge_answer_command.py +2 -0
- rasa/dialogue_understanding/commands/repeat_bot_messages_command.py +2 -0
- rasa/dialogue_understanding/commands/set_slot_command.py +15 -5
- rasa/dialogue_understanding/commands/skip_question_command.py +3 -3
- rasa/dialogue_understanding/commands/start_flow_command.py +7 -3
- rasa/dialogue_understanding/commands/utils.py +26 -2
- rasa/dialogue_understanding/generator/__init__.py +7 -1
- rasa/dialogue_understanding/generator/command_generator.py +15 -3
- rasa/dialogue_understanding/generator/command_parser.py +2 -2
- rasa/dialogue_understanding/generator/command_parser_validator.py +63 -0
- rasa/dialogue_understanding/generator/constants.py +2 -2
- rasa/dialogue_understanding/generator/nlu_command_adapter.py +2 -2
- rasa/dialogue_understanding/generator/prompt_templates/command_prompt_template.jinja2 +0 -2
- rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_claude_3_5_sonnet_20240620_template.jinja2 +1 -0
- rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_gpt_4o_2024_11_20_template.jinja2 +1 -0
- rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v3_claude_3_5_sonnet_20240620_template.jinja2 +79 -0
- rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v3_gpt_4o_2024_11_20_template.jinja2 +79 -0
- rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +28 -463
- rasa/dialogue_understanding/generator/single_step/search_ready_llm_command_generator.py +147 -0
- rasa/dialogue_understanding/generator/single_step/single_step_based_llm_command_generator.py +461 -0
- rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +11 -64
- rasa/dialogue_understanding/patterns/cancel.py +1 -2
- rasa/dialogue_understanding/patterns/clarify.py +1 -1
- rasa/dialogue_understanding/patterns/correction.py +2 -2
- rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +37 -25
- rasa/dialogue_understanding/patterns/domain_for_patterns.py +190 -0
- rasa/dialogue_understanding/processor/command_processor.py +11 -12
- rasa/dialogue_understanding/processor/command_processor_component.py +3 -3
- rasa/dialogue_understanding/stack/frames/flow_stack_frame.py +17 -4
- rasa/dialogue_understanding/stack/utils.py +3 -1
- rasa/dialogue_understanding/utils.py +68 -12
- rasa/dialogue_understanding_test/du_test_case.py +1 -1
- rasa/dialogue_understanding_test/du_test_runner.py +4 -22
- rasa/dialogue_understanding_test/test_case_simulation/test_case_tracker_simulator.py +2 -6
- rasa/e2e_test/e2e_test_coverage_report.py +1 -1
- rasa/e2e_test/e2e_test_runner.py +1 -1
- rasa/engine/constants.py +1 -1
- rasa/engine/graph.py +2 -2
- rasa/engine/recipes/default_recipe.py +26 -2
- rasa/engine/validation.py +3 -2
- rasa/hooks.py +0 -28
- rasa/llm_fine_tuning/annotation_module.py +39 -9
- rasa/llm_fine_tuning/conversations.py +3 -0
- rasa/llm_fine_tuning/llm_data_preparation_module.py +66 -49
- rasa/llm_fine_tuning/paraphrasing/conversation_rephraser.py +5 -7
- rasa/llm_fine_tuning/paraphrasing/rephrase_validator.py +52 -44
- rasa/llm_fine_tuning/paraphrasing_module.py +10 -12
- rasa/llm_fine_tuning/storage.py +4 -4
- rasa/llm_fine_tuning/utils.py +63 -1
- rasa/model_manager/model_api.py +88 -0
- rasa/model_manager/trainer_service.py +4 -4
- rasa/plugin.py +1 -11
- rasa/privacy/__init__.py +0 -0
- rasa/privacy/constants.py +83 -0
- rasa/privacy/event_broker_utils.py +77 -0
- rasa/privacy/privacy_config.py +281 -0
- rasa/privacy/privacy_config_schema.json +86 -0
- rasa/privacy/privacy_filter.py +340 -0
- rasa/privacy/privacy_manager.py +576 -0
- rasa/server.py +23 -2
- rasa/shared/constants.py +18 -0
- rasa/shared/core/command_payload_reader.py +1 -5
- rasa/shared/core/constants.py +4 -3
- rasa/shared/core/domain.py +7 -0
- rasa/shared/core/events.py +38 -10
- rasa/shared/core/flows/constants.py +2 -0
- rasa/shared/core/flows/flow.py +127 -14
- rasa/shared/core/flows/flows_list.py +18 -1
- rasa/shared/core/flows/flows_yaml_schema.json +3 -0
- rasa/shared/core/flows/steps/collect.py +46 -2
- rasa/shared/core/flows/steps/link.py +7 -2
- rasa/shared/core/flows/validation.py +25 -5
- rasa/shared/core/slots.py +28 -0
- rasa/shared/core/training_data/story_reader/yaml_story_reader.py +1 -4
- rasa/shared/exceptions.py +4 -0
- rasa/shared/providers/_configs/azure_openai_client_config.py +6 -2
- rasa/shared/providers/_configs/default_litellm_client_config.py +1 -1
- rasa/shared/providers/_configs/huggingface_local_embedding_client_config.py +1 -1
- rasa/shared/providers/_configs/openai_client_config.py +5 -1
- rasa/shared/providers/_configs/rasa_llm_client_config.py +1 -1
- rasa/shared/providers/_configs/self_hosted_llm_client_config.py +1 -1
- rasa/shared/providers/_configs/utils.py +0 -99
- rasa/shared/providers/embedding/_base_litellm_embedding_client.py +3 -0
- rasa/shared/providers/llm/_base_litellm_client.py +5 -2
- rasa/shared/utils/common.py +1 -1
- rasa/shared/utils/configs.py +110 -0
- rasa/shared/utils/constants.py +0 -3
- rasa/shared/utils/llm.py +195 -9
- rasa/shared/utils/pykwalify_extensions.py +0 -9
- rasa/shared/utils/yaml.py +32 -0
- rasa/studio/constants.py +1 -0
- rasa/studio/data_handler.py +11 -4
- rasa/studio/download.py +167 -0
- rasa/studio/link.py +200 -0
- rasa/studio/prompts.py +223 -0
- rasa/studio/pull/__init__.py +0 -0
- rasa/studio/{download/flows.py → pull/data.py} +23 -160
- rasa/studio/{download → pull}/domains.py +1 -1
- rasa/studio/pull/pull.py +235 -0
- rasa/studio/push.py +136 -0
- rasa/studio/train.py +1 -1
- rasa/studio/upload.py +117 -67
- rasa/telemetry.py +82 -25
- rasa/tracing/config.py +3 -4
- rasa/tracing/constants.py +19 -1
- rasa/tracing/instrumentation/attribute_extractors.py +30 -8
- rasa/tracing/instrumentation/instrumentation.py +53 -2
- rasa/tracing/instrumentation/metrics.py +98 -15
- rasa/tracing/metric_instrument_provider.py +75 -3
- rasa/utils/common.py +7 -22
- rasa/utils/log_utils.py +1 -45
- rasa/validator.py +2 -8
- rasa/version.py +1 -1
- {rasa_pro-3.13.0.dev20250612.dist-info → rasa_pro-3.13.0rc1.dist-info}/METADATA +8 -9
- {rasa_pro-3.13.0.dev20250612.dist-info → rasa_pro-3.13.0rc1.dist-info}/RECORD +241 -220
- rasa/anonymization/__init__.py +0 -2
- rasa/anonymization/anonymisation_rule_yaml_reader.py +0 -91
- rasa/anonymization/anonymization_pipeline.py +0 -286
- rasa/anonymization/anonymization_rule_executor.py +0 -266
- rasa/anonymization/anonymization_rule_orchestrator.py +0 -119
- rasa/anonymization/schemas/config.yml +0 -47
- rasa/anonymization/utils.py +0 -118
- rasa/core/channels/inspector/dist/assets/channel-3730f5fd.js +0 -1
- rasa/core/channels/inspector/dist/assets/clone-e847561e.js +0 -1
- rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-efbbfe00.js +0 -1
- rasa/studio/download/download.py +0 -439
- /rasa/{studio/download → core/information_retrieval/ingestion}/__init__.py +0 -0
- {rasa_pro-3.13.0.dev20250612.dist-info → rasa_pro-3.13.0rc1.dist-info}/NOTICE +0 -0
- {rasa_pro-3.13.0.dev20250612.dist-info → rasa_pro-3.13.0rc1.dist-info}/WHEEL +0 -0
- {rasa_pro-3.13.0.dev20250612.dist-info → rasa_pro-3.13.0rc1.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"""Utilities for parsing FAQ-style documents (Q/A pairs) used in extractive search."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from collections import defaultdict
|
|
5
|
+
from typing import TYPE_CHECKING, List
|
|
6
|
+
|
|
7
|
+
import structlog
|
|
8
|
+
|
|
9
|
+
from rasa.shared.constants import (
|
|
10
|
+
DOCUMENT_TYPE_FAQ,
|
|
11
|
+
FAQ_DOCUMENT_ENTRY_SEPARATOR,
|
|
12
|
+
FAQ_DOCUMENT_LINE_SEPARATOR,
|
|
13
|
+
FAQ_DOCUMENT_METADATA_ANSWER,
|
|
14
|
+
FAQ_DOCUMENT_METADATA_TITLE,
|
|
15
|
+
FAQ_DOCUMENT_METADATA_TYPE,
|
|
16
|
+
FAQ_INPUT_DATA_ANSWER_LINE_PREFIX,
|
|
17
|
+
FAQ_INPUT_DATA_QUESTION_LINE_PREFIX,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
from langchain.schema import Document
|
|
22
|
+
|
|
23
|
+
_FAQ_PAIR_PATTERN = re.compile(
|
|
24
|
+
rf"{re.escape(FAQ_INPUT_DATA_QUESTION_LINE_PREFIX)}\s*"
|
|
25
|
+
rf"(?P<question>.*?)\s*{FAQ_DOCUMENT_LINE_SEPARATOR}\s*"
|
|
26
|
+
rf"{re.escape(FAQ_INPUT_DATA_ANSWER_LINE_PREFIX)}\s*"
|
|
27
|
+
rf"(?P<answer>.*)",
|
|
28
|
+
re.DOTALL,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
structlogger = structlog.get_logger()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _format_faq_documents(documents: List["Document"]) -> List["Document"]:
|
|
36
|
+
"""Splits each loaded file into individual FAQs.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
documents: Documents representing whole files containing FAQs.
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
List of Document objects, each containing a separate FAQ.
|
|
43
|
+
|
|
44
|
+
Examples:
|
|
45
|
+
An example of a file containing FAQs:
|
|
46
|
+
|
|
47
|
+
Q: Who is Finley?
|
|
48
|
+
A: Finley is your smart assistant for the FinX App. You can add him to your
|
|
49
|
+
favorite messenger and tell him what you need help with.
|
|
50
|
+
|
|
51
|
+
Q: How does Finley work?
|
|
52
|
+
A: Finley is powered by the latest chatbot technology leveraging a unique
|
|
53
|
+
interplay of large language models and secure logic.
|
|
54
|
+
|
|
55
|
+
More details in documentation: https://rasa.com/docs/reference/config/policies/extractive-search/
|
|
56
|
+
"""
|
|
57
|
+
structured_faqs = []
|
|
58
|
+
from langchain.schema import Document
|
|
59
|
+
|
|
60
|
+
for document in documents:
|
|
61
|
+
chunks = document.page_content.strip().split(FAQ_DOCUMENT_ENTRY_SEPARATOR)
|
|
62
|
+
|
|
63
|
+
for chunk in chunks:
|
|
64
|
+
match = _FAQ_PAIR_PATTERN.match(chunk.strip())
|
|
65
|
+
|
|
66
|
+
if not match:
|
|
67
|
+
structlogger.warning(
|
|
68
|
+
"faq_parser.format_faq_documents.invalid_chunk_skipped",
|
|
69
|
+
event_info=(
|
|
70
|
+
"Chunk does not match expected QA format. "
|
|
71
|
+
"Please refer to the documentation: "
|
|
72
|
+
"https://rasa.com/docs/reference/config/"
|
|
73
|
+
"policies/extractive-search/"
|
|
74
|
+
),
|
|
75
|
+
chunk_preview=chunk[:100],
|
|
76
|
+
)
|
|
77
|
+
continue
|
|
78
|
+
|
|
79
|
+
question = match.group("question").strip()
|
|
80
|
+
answer = match.group("answer").strip()
|
|
81
|
+
title = _sanitize_title(question)
|
|
82
|
+
|
|
83
|
+
formatted_document = Document(
|
|
84
|
+
page_content=question,
|
|
85
|
+
metadata={
|
|
86
|
+
FAQ_DOCUMENT_METADATA_TITLE: title,
|
|
87
|
+
FAQ_DOCUMENT_METADATA_TYPE: DOCUMENT_TYPE_FAQ,
|
|
88
|
+
FAQ_DOCUMENT_METADATA_ANSWER: answer,
|
|
89
|
+
},
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
structured_faqs.append(formatted_document)
|
|
93
|
+
|
|
94
|
+
structlogger.debug(
|
|
95
|
+
"faq_parser.format_faq_documents.parsed_chunk",
|
|
96
|
+
event_info="Parsed chunk.",
|
|
97
|
+
title=title,
|
|
98
|
+
question=question,
|
|
99
|
+
answer=answer,
|
|
100
|
+
parsed_chunk_preview=chunk[:100],
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
structlogger.debug(
|
|
104
|
+
"faq_parser.format_faq_documents.parsed_chunks",
|
|
105
|
+
event_info=(
|
|
106
|
+
f"Retrieved {len(structured_faqs)} FAQ pair(s)"
|
|
107
|
+
f"from {len(documents)} document(s)."
|
|
108
|
+
),
|
|
109
|
+
num_structured_faqs=len(structured_faqs),
|
|
110
|
+
num_documents=len(documents),
|
|
111
|
+
)
|
|
112
|
+
_check_and_parsed_faq_documents_for_duplicates(structured_faqs)
|
|
113
|
+
return structured_faqs
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _sanitize_title(title: str) -> str:
|
|
117
|
+
title = title.lower()
|
|
118
|
+
# Remove all whitespaces with "_"
|
|
119
|
+
title = re.sub(r"\s+", "_", title)
|
|
120
|
+
# Remove all non alpha-numeric characters
|
|
121
|
+
title = re.sub(r"[^\w]", "", title)
|
|
122
|
+
# Collapse multiple "_"
|
|
123
|
+
title = re.sub(r"_+", "_", title)
|
|
124
|
+
# Clean up edges
|
|
125
|
+
return title.strip("_")
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _check_and_parsed_faq_documents_for_duplicates(documents: List["Document"]) -> None:
|
|
129
|
+
seen_qa_pairs = set()
|
|
130
|
+
seen_questions: defaultdict = defaultdict(list)
|
|
131
|
+
|
|
132
|
+
for doc in documents:
|
|
133
|
+
question = doc.page_content.strip()
|
|
134
|
+
answer = doc.metadata.get(FAQ_DOCUMENT_METADATA_ANSWER, "").strip()
|
|
135
|
+
|
|
136
|
+
if not question or not answer:
|
|
137
|
+
continue
|
|
138
|
+
|
|
139
|
+
if (question, answer) in seen_qa_pairs:
|
|
140
|
+
structlogger.warning(
|
|
141
|
+
"faq_parser.duplicate_qa_pair_found",
|
|
142
|
+
event_info="Duplicate QA pair found.",
|
|
143
|
+
question=question,
|
|
144
|
+
answer_preview=answer,
|
|
145
|
+
)
|
|
146
|
+
continue
|
|
147
|
+
|
|
148
|
+
if question in seen_questions and seen_questions[question] != answer:
|
|
149
|
+
structlogger.warning(
|
|
150
|
+
"faq_parser.inconsistent_answer",
|
|
151
|
+
event_info="Duplicate question with different answer found.",
|
|
152
|
+
question=question,
|
|
153
|
+
previous_answers=seen_questions[question],
|
|
154
|
+
new_answer=answer,
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
seen_qa_pairs.add((question, answer))
|
|
158
|
+
seen_questions[question].append(answer)
|
rasa/core/jobs.py
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import logging
|
|
3
|
+
from typing import Optional
|
|
3
4
|
|
|
4
5
|
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
|
5
6
|
from pytz import UnknownTimeZoneError, utc
|
|
6
7
|
|
|
7
8
|
import rasa.shared.utils.io
|
|
8
9
|
|
|
9
|
-
__scheduler = None
|
|
10
|
+
__scheduler: Optional[AsyncIOScheduler] = None
|
|
10
11
|
|
|
11
12
|
logger = logging.getLogger(__name__)
|
|
12
13
|
|
|
@@ -5,15 +5,21 @@ from jinja2 import Template
|
|
|
5
5
|
|
|
6
6
|
from rasa import telemetry
|
|
7
7
|
from rasa.core.nlg.response import TemplatedNaturalLanguageGenerator
|
|
8
|
-
from rasa.core.nlg.summarize import
|
|
8
|
+
from rasa.core.nlg.summarize import (
|
|
9
|
+
_count_multiple_utterances_as_single_turn,
|
|
10
|
+
summarize_conversation,
|
|
11
|
+
)
|
|
9
12
|
from rasa.shared.constants import (
|
|
10
13
|
LLM_CONFIG_KEY,
|
|
14
|
+
MAX_COMPLETION_TOKENS_CONFIG_KEY,
|
|
11
15
|
MODEL_CONFIG_KEY,
|
|
12
16
|
MODEL_GROUP_ID_CONFIG_KEY,
|
|
13
17
|
MODEL_NAME_CONFIG_KEY,
|
|
14
18
|
OPENAI_PROVIDER,
|
|
15
19
|
PROMPT_CONFIG_KEY,
|
|
20
|
+
PROMPT_TEMPLATE_CONFIG_KEY,
|
|
16
21
|
PROVIDER_CONFIG_KEY,
|
|
22
|
+
TEMPERATURE_CONFIG_KEY,
|
|
17
23
|
TIMEOUT_CONFIG_KEY,
|
|
18
24
|
)
|
|
19
25
|
from rasa.shared.core.domain import KEY_RESPONSES_TEXT, Domain
|
|
@@ -33,6 +39,7 @@ from rasa.shared.utils.llm import (
|
|
|
33
39
|
DEFAULT_OPENAI_GENERATE_MODEL_NAME,
|
|
34
40
|
DEFAULT_OPENAI_MAX_GENERATED_TOKENS,
|
|
35
41
|
USER,
|
|
42
|
+
check_prompt_config_keys_and_warn_if_deprecated,
|
|
36
43
|
combine_custom_and_default_config,
|
|
37
44
|
get_prompt_template,
|
|
38
45
|
llm_factory,
|
|
@@ -53,12 +60,13 @@ RESPONSE_SUMMARISE_CONVERSATION_KEY = "summarize_conversation"
|
|
|
53
60
|
DEFAULT_REPHRASE_ALL = False
|
|
54
61
|
DEFAULT_SUMMARIZE_HISTORY = True
|
|
55
62
|
DEFAULT_MAX_HISTORICAL_TURNS = 5
|
|
63
|
+
DEFAULT_COUNT_MULTIPLE_UTTERANCES_AS_SINGLE_TURN = True
|
|
56
64
|
|
|
57
65
|
DEFAULT_LLM_CONFIG = {
|
|
58
66
|
PROVIDER_CONFIG_KEY: OPENAI_PROVIDER,
|
|
59
67
|
MODEL_CONFIG_KEY: DEFAULT_OPENAI_GENERATE_MODEL_NAME,
|
|
60
|
-
|
|
61
|
-
|
|
68
|
+
TEMPERATURE_CONFIG_KEY: 0.3,
|
|
69
|
+
MAX_COMPLETION_TOKENS_CONFIG_KEY: DEFAULT_OPENAI_MAX_GENERATED_TOKENS,
|
|
62
70
|
TIMEOUT_CONFIG_KEY: 5,
|
|
63
71
|
}
|
|
64
72
|
|
|
@@ -70,6 +78,7 @@ its meaning. Use simple {{language}}.
|
|
|
70
78
|
Context / previous conversation with the user:
|
|
71
79
|
{{history}}
|
|
72
80
|
|
|
81
|
+
Last user message:
|
|
73
82
|
{{current_input}}
|
|
74
83
|
|
|
75
84
|
Suggested AI Response: {{suggested_response}}
|
|
@@ -103,8 +112,15 @@ class ContextualResponseRephraser(
|
|
|
103
112
|
super().__init__(domain.responses)
|
|
104
113
|
|
|
105
114
|
self.nlg_endpoint = endpoint_config
|
|
115
|
+
|
|
116
|
+
# Warn if the prompt config key is used to set the prompt template
|
|
117
|
+
check_prompt_config_keys_and_warn_if_deprecated(
|
|
118
|
+
self.nlg_endpoint.kwargs, "contextual_response_rephraser"
|
|
119
|
+
)
|
|
120
|
+
|
|
106
121
|
self.prompt_template = get_prompt_template(
|
|
107
|
-
self.nlg_endpoint.kwargs.get(
|
|
122
|
+
self.nlg_endpoint.kwargs.get(PROMPT_TEMPLATE_CONFIG_KEY)
|
|
123
|
+
or self.nlg_endpoint.kwargs.get(PROMPT_CONFIG_KEY),
|
|
108
124
|
DEFAULT_RESPONSE_VARIATION_PROMPT_TEMPLATE,
|
|
109
125
|
log_source_component=ContextualResponseRephraser.__name__,
|
|
110
126
|
log_source_method=LOG_COMPONENT_SOURCE_METHOD_INIT,
|
|
@@ -122,6 +138,11 @@ class ContextualResponseRephraser(
|
|
|
122
138
|
"max_historical_turns", DEFAULT_MAX_HISTORICAL_TURNS
|
|
123
139
|
)
|
|
124
140
|
|
|
141
|
+
self.count_multiple_utterances_as_single_turn = self.nlg_endpoint.kwargs.get(
|
|
142
|
+
"count_multiple_utterances_as_single_turn",
|
|
143
|
+
DEFAULT_COUNT_MULTIPLE_UTTERANCES_AS_SINGLE_TURN,
|
|
144
|
+
)
|
|
145
|
+
|
|
125
146
|
self.llm_config = resolve_model_client_config(
|
|
126
147
|
self.nlg_endpoint.kwargs.get(LLM_CONFIG_KEY),
|
|
127
148
|
ContextualResponseRephraser.__name__,
|
|
@@ -258,8 +279,16 @@ class ContextualResponseRephraser(
|
|
|
258
279
|
Returns:
|
|
259
280
|
The history for the prompt.
|
|
260
281
|
"""
|
|
282
|
+
# Count multiple utterances by bot/user as single turn in conversation history
|
|
283
|
+
turns_wrapper = (
|
|
284
|
+
_count_multiple_utterances_as_single_turn
|
|
285
|
+
if self.count_multiple_utterances_as_single_turn
|
|
286
|
+
else None
|
|
287
|
+
)
|
|
261
288
|
llm = llm_factory(self.llm_config, DEFAULT_LLM_CONFIG)
|
|
262
|
-
return await summarize_conversation(
|
|
289
|
+
return await summarize_conversation(
|
|
290
|
+
tracker, llm, max_turns=5, turns_wrapper=turns_wrapper
|
|
291
|
+
)
|
|
263
292
|
|
|
264
293
|
async def rephrase(
|
|
265
294
|
self,
|
|
@@ -281,19 +310,26 @@ class ContextualResponseRephraser(
|
|
|
281
310
|
|
|
282
311
|
prompt_template_text = self._template_for_response_rephrasing(response)
|
|
283
312
|
|
|
284
|
-
#
|
|
285
|
-
|
|
286
|
-
current_input =
|
|
313
|
+
# Last user message (=current input) should always be in prompt if available
|
|
314
|
+
last_message_by_user = getattr(tracker.latest_message, "text", "")
|
|
315
|
+
current_input = (
|
|
316
|
+
f"{USER}: {last_message_by_user}" if last_message_by_user else ""
|
|
317
|
+
)
|
|
287
318
|
|
|
288
319
|
# Only summarise conversation history if flagged
|
|
289
320
|
if self.summarize_history:
|
|
290
321
|
history = await self._create_history(tracker)
|
|
291
322
|
else:
|
|
292
|
-
#
|
|
323
|
+
# Count multiple utterances by bot/user as single turn
|
|
324
|
+
turns_wrapper = (
|
|
325
|
+
_count_multiple_utterances_as_single_turn
|
|
326
|
+
if self.count_multiple_utterances_as_single_turn
|
|
327
|
+
else None
|
|
328
|
+
)
|
|
293
329
|
max_turns = max(self.max_historical_turns, 1)
|
|
294
|
-
history = tracker_as_readable_transcript(
|
|
295
|
-
|
|
296
|
-
|
|
330
|
+
history = tracker_as_readable_transcript(
|
|
331
|
+
tracker, max_turns=max_turns, turns_wrapper=turns_wrapper
|
|
332
|
+
)
|
|
297
333
|
|
|
298
334
|
prompt = Template(prompt_template_text).render(
|
|
299
335
|
history=history,
|
rasa/core/nlg/generator.py
CHANGED
|
@@ -292,7 +292,6 @@ def _evaluate_predicate(constraint: str, filled_slots: Dict[Text, Any]) -> bool:
|
|
|
292
292
|
structlogger.error(
|
|
293
293
|
"rasa.core.nlg.generator.evaluate_conditional_response_predicate.error",
|
|
294
294
|
predicate=constraint,
|
|
295
|
-
document=document,
|
|
296
295
|
error=str(e),
|
|
297
296
|
)
|
|
298
297
|
return False
|
rasa/core/nlg/interpolator.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import copy
|
|
2
1
|
import logging
|
|
3
2
|
import re
|
|
4
3
|
from typing import Any, Dict, List, Text, Union
|
|
@@ -70,9 +69,9 @@ def interpolate_format_template(response: Text, values: Dict[Text, Text]) -> Tex
|
|
|
70
69
|
)
|
|
71
70
|
structlogger.exception(
|
|
72
71
|
"interpolator.interpolate.text",
|
|
73
|
-
response=copy.deepcopy(response),
|
|
74
72
|
placeholder_key=e.args[0],
|
|
75
73
|
event_info=event_info,
|
|
74
|
+
error=str(e),
|
|
76
75
|
)
|
|
77
76
|
return response
|
|
78
77
|
|
|
@@ -98,9 +97,9 @@ def interpolate_jinja_template(response: Text, values: Dict[Text, Any]) -> Text:
|
|
|
98
97
|
)
|
|
99
98
|
structlogger.exception(
|
|
100
99
|
"interpolator.interpolate.text",
|
|
101
|
-
response=copy.deepcopy(response),
|
|
102
100
|
placeholder_key=e.args[0],
|
|
103
101
|
event_info=event_info,
|
|
102
|
+
error=str(e),
|
|
104
103
|
)
|
|
105
104
|
return response
|
|
106
105
|
|
rasa/core/nlg/summarize.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
from
|
|
1
|
+
from itertools import groupby
|
|
2
|
+
from typing import Callable, List, Optional
|
|
2
3
|
|
|
3
4
|
import structlog
|
|
4
5
|
from jinja2 import Template
|
|
@@ -23,20 +24,49 @@ SUMMARY_PROMPT_TEMPLATE = Template(_DEFAULT_SUMMARIZER_TEMPLATE)
|
|
|
23
24
|
MAX_TURNS_DEFAULT = 20
|
|
24
25
|
|
|
25
26
|
|
|
27
|
+
def _count_multiple_utterances_as_single_turn(transcript: List[str]) -> List[str]:
|
|
28
|
+
"""Counts multiple utterances as a single turn.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
transcript: the lines of the transcript
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
transcript: with multiple utterances counted as a single turn
|
|
35
|
+
"""
|
|
36
|
+
if not transcript:
|
|
37
|
+
return []
|
|
38
|
+
|
|
39
|
+
def get_speaker_label(line: str) -> str:
|
|
40
|
+
return line.partition(": ")[0] if ": " in line else ""
|
|
41
|
+
|
|
42
|
+
modified_transcript = [
|
|
43
|
+
f"{speaker}: {' '.join(line.partition(': ')[2] for line in group)}"
|
|
44
|
+
for speaker, group in groupby(transcript, key=get_speaker_label)
|
|
45
|
+
if speaker
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
return modified_transcript
|
|
49
|
+
|
|
50
|
+
|
|
26
51
|
def _create_summarization_prompt(
|
|
27
|
-
tracker: DialogueStateTracker,
|
|
52
|
+
tracker: DialogueStateTracker,
|
|
53
|
+
max_turns: Optional[int],
|
|
54
|
+
turns_wrapper: Optional[Callable[[List[str]], List[str]]],
|
|
28
55
|
) -> str:
|
|
29
56
|
"""Creates an LLM prompt to summarize the conversation in the tracker.
|
|
30
57
|
|
|
31
58
|
Args:
|
|
32
59
|
tracker: tracker of the conversation to be summarized
|
|
33
60
|
max_turns: maximum number of turns to summarize
|
|
61
|
+
turns_wrapper: optional function to wrap the turns
|
|
34
62
|
|
|
35
63
|
|
|
36
64
|
Returns:
|
|
37
65
|
The prompt to summarize the conversation.
|
|
38
66
|
"""
|
|
39
|
-
transcript = tracker_as_readable_transcript(
|
|
67
|
+
transcript = tracker_as_readable_transcript(
|
|
68
|
+
tracker, max_turns=max_turns, turns_wrapper=turns_wrapper
|
|
69
|
+
)
|
|
40
70
|
return SUMMARY_PROMPT_TEMPLATE.render(
|
|
41
71
|
conversation=transcript,
|
|
42
72
|
)
|
|
@@ -46,6 +76,7 @@ async def summarize_conversation(
|
|
|
46
76
|
tracker: DialogueStateTracker,
|
|
47
77
|
llm: LLMClient,
|
|
48
78
|
max_turns: Optional[int] = MAX_TURNS_DEFAULT,
|
|
79
|
+
turns_wrapper: Optional[Callable[[List[str]], List[str]]] = None,
|
|
49
80
|
) -> str:
|
|
50
81
|
"""Summarizes the dialogue using the LLM.
|
|
51
82
|
|
|
@@ -53,11 +84,12 @@ async def summarize_conversation(
|
|
|
53
84
|
tracker: the tracker to summarize
|
|
54
85
|
llm: the LLM to use for summarization
|
|
55
86
|
max_turns: maximum number of turns to summarize
|
|
87
|
+
turns_wrapper: optional function to wrap the turns
|
|
56
88
|
|
|
57
89
|
Returns:
|
|
58
90
|
The summary of the dialogue.
|
|
59
91
|
"""
|
|
60
|
-
prompt = _create_summarization_prompt(tracker, max_turns)
|
|
92
|
+
prompt = _create_summarization_prompt(tracker, max_turns, turns_wrapper)
|
|
61
93
|
try:
|
|
62
94
|
llm_response = await llm.acompletion(prompt)
|
|
63
95
|
summarization = llm_response.choices[0].strip()
|
|
@@ -66,6 +98,8 @@ async def summarize_conversation(
|
|
|
66
98
|
)
|
|
67
99
|
return summarization
|
|
68
100
|
except Exception as e:
|
|
69
|
-
transcript = tracker_as_readable_transcript(
|
|
101
|
+
transcript = tracker_as_readable_transcript(
|
|
102
|
+
tracker, max_turns=max_turns, turns_wrapper=turns_wrapper
|
|
103
|
+
)
|
|
70
104
|
structlogger.error("summarization.error", error=e)
|
|
71
105
|
return transcript
|