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,77 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, List, Optional
|
|
2
|
+
|
|
3
|
+
import structlog
|
|
4
|
+
|
|
5
|
+
from rasa.core.brokers.broker import EventBroker
|
|
6
|
+
from rasa.utils.endpoints import EndpointConfig
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from asyncio import AbstractEventLoop
|
|
10
|
+
|
|
11
|
+
structlogger = structlog.get_logger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
async def create_event_brokers(
|
|
15
|
+
event_broker_endpoint: Optional[EndpointConfig],
|
|
16
|
+
event_loop: Optional["AbstractEventLoop"] = None,
|
|
17
|
+
) -> List[EventBroker]:
|
|
18
|
+
"""Create EventBroker objects for each anonymization topic or queue."""
|
|
19
|
+
if event_broker_endpoint is None or event_broker_endpoint.type is None:
|
|
20
|
+
structlogger.debug(
|
|
21
|
+
"rasa.privacy_filtering.create_event_broker.no_event_broker_type",
|
|
22
|
+
)
|
|
23
|
+
return []
|
|
24
|
+
|
|
25
|
+
if event_broker_endpoint.type == "kafka":
|
|
26
|
+
event_collection = event_broker_endpoint.kwargs.get("anonymization_topics", [])
|
|
27
|
+
event_collection_type = "topic"
|
|
28
|
+
elif event_broker_endpoint.type == "pika":
|
|
29
|
+
event_collection = event_broker_endpoint.kwargs.get("anonymization_queues", [])
|
|
30
|
+
event_collection_type = "queues"
|
|
31
|
+
else:
|
|
32
|
+
structlogger.debug(
|
|
33
|
+
"rasa.privacy_filtering.create_event_broker.unsupported_event_broker_type",
|
|
34
|
+
event_broker_type=event_broker_endpoint.type,
|
|
35
|
+
)
|
|
36
|
+
return []
|
|
37
|
+
|
|
38
|
+
if not event_collection:
|
|
39
|
+
structlogger.debug(
|
|
40
|
+
f"rasa.privacy_filtering.create_event_broker.no_anonymization_{event_collection_type}",
|
|
41
|
+
event_collection_type=event_collection_type,
|
|
42
|
+
)
|
|
43
|
+
return []
|
|
44
|
+
|
|
45
|
+
return await _create_event_brokers(
|
|
46
|
+
event_broker_endpoint, event_collection, event_collection_type, event_loop
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
async def _create_event_brokers(
|
|
51
|
+
event_broker_endpoint: EndpointConfig,
|
|
52
|
+
event_collection: List[str],
|
|
53
|
+
event_collection_type: str,
|
|
54
|
+
event_loop: Optional["AbstractEventLoop"] = None,
|
|
55
|
+
) -> List[EventBroker]:
|
|
56
|
+
"""Create event brokers."""
|
|
57
|
+
event_brokers = []
|
|
58
|
+
for item in event_collection:
|
|
59
|
+
event_broker_endpoint.kwargs[event_collection_type] = (
|
|
60
|
+
item if event_collection_type == "topic" else [item]
|
|
61
|
+
)
|
|
62
|
+
structlogger.debug(
|
|
63
|
+
"rasa.privacy_filtering.create_event_broker",
|
|
64
|
+
event_info=f"Setting anonymized event streaming to '{item}'.",
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
event_broker = await EventBroker.create(event_broker_endpoint, event_loop)
|
|
68
|
+
if event_broker is None:
|
|
69
|
+
structlogger.debug(
|
|
70
|
+
"rasa.privacy_filtering.create_event_broker.no_event_broker_created",
|
|
71
|
+
event_info=f"No event broker created for publishing to '{item}'.",
|
|
72
|
+
)
|
|
73
|
+
continue
|
|
74
|
+
|
|
75
|
+
event_brokers.append(event_broker)
|
|
76
|
+
|
|
77
|
+
return event_brokers
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
from enum import Enum
|
|
6
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Optional
|
|
7
|
+
|
|
8
|
+
import structlog
|
|
9
|
+
from apscheduler.triggers.cron import CronTrigger
|
|
10
|
+
from pydantic import BaseModel, ConfigDict
|
|
11
|
+
|
|
12
|
+
from rasa.constants import PACKAGE_NAME
|
|
13
|
+
from rasa.privacy.constants import (
|
|
14
|
+
ANONYMIZATION_KEY,
|
|
15
|
+
DELETION_KEY,
|
|
16
|
+
KEEP_LEFT_KEY,
|
|
17
|
+
KEEP_RIGHT_KEY,
|
|
18
|
+
PRIVACY_CONFIG_SCHEMA,
|
|
19
|
+
REDACTION_CHAR_KEY,
|
|
20
|
+
SLOT_KEY,
|
|
21
|
+
TRACKER_STORE_SETTINGS,
|
|
22
|
+
USER_CHAT_INACTIVITY_IN_MINUTES_ENV_VAR_NAME,
|
|
23
|
+
)
|
|
24
|
+
from rasa.shared.exceptions import RasaException
|
|
25
|
+
from rasa.shared.utils.io import read_json_file
|
|
26
|
+
from rasa.shared.utils.yaml import (
|
|
27
|
+
YamlValidationException,
|
|
28
|
+
validate_data_with_jsonschema,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
if TYPE_CHECKING:
|
|
32
|
+
from rasa.shared.core.domain import Domain
|
|
33
|
+
|
|
34
|
+
structlogger = structlog.get_logger(__name__)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class AnonymizationType(Enum):
|
|
38
|
+
"""Enum for the anonymization types."""
|
|
39
|
+
|
|
40
|
+
REDACT = "redact"
|
|
41
|
+
"""Replaces the PII plaintext value with the same character
|
|
42
|
+
for the entire or partial length of the value."""
|
|
43
|
+
MASK = "mask"
|
|
44
|
+
"""Replaces the PII plaintext value with the uppercase slot name
|
|
45
|
+
in square brackets, e.g. [CREDIT_CARD_NUMBER]."""
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class AnonymizationMethod(BaseModel):
|
|
49
|
+
"""Class for configuring the anonymization method."""
|
|
50
|
+
|
|
51
|
+
method_type: AnonymizationType
|
|
52
|
+
"""The anonymization method to be used."""
|
|
53
|
+
redaction_char: str
|
|
54
|
+
"""The character to use for redaction."""
|
|
55
|
+
keep_left: Optional[int] = None
|
|
56
|
+
"""The number of characters to be kept intact on the left side."""
|
|
57
|
+
keep_right: Optional[int] = None
|
|
58
|
+
"""The number of characters to be kept intact on the right side."""
|
|
59
|
+
|
|
60
|
+
@classmethod
|
|
61
|
+
def from_dict(cls, data: Dict[str, Any]) -> AnonymizationMethod:
|
|
62
|
+
"""Create an AnonymizationMethod object from parsed data."""
|
|
63
|
+
method_type = AnonymizationType(
|
|
64
|
+
data.get("type", AnonymizationType.REDACT.value)
|
|
65
|
+
)
|
|
66
|
+
redaction_char = data.get(REDACTION_CHAR_KEY, "*")
|
|
67
|
+
keep_left = data.get(KEEP_LEFT_KEY)
|
|
68
|
+
keep_right = data.get(KEEP_RIGHT_KEY)
|
|
69
|
+
|
|
70
|
+
return cls(
|
|
71
|
+
method_type=method_type,
|
|
72
|
+
redaction_char=redaction_char,
|
|
73
|
+
keep_left=keep_left,
|
|
74
|
+
keep_right=keep_right,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class PrivacyPolicy(BaseModel):
|
|
79
|
+
"""Parent class for configuring privacy policies."""
|
|
80
|
+
|
|
81
|
+
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
82
|
+
|
|
83
|
+
min_after_session_end: int
|
|
84
|
+
"""Minimum time in minutes after session end before the policy is executed."""
|
|
85
|
+
cron: CronTrigger
|
|
86
|
+
"""Cron trigger for periodic execution of the privacy policy."""
|
|
87
|
+
|
|
88
|
+
@classmethod
|
|
89
|
+
def from_dict(cls, data: Dict[str, Any]) -> PrivacyPolicy:
|
|
90
|
+
"""Create an AnonymizationPolicy object from parsed data."""
|
|
91
|
+
min_after_session_end = data.get("min_after_session_end", 1)
|
|
92
|
+
validate_min_after_session_end(min_after_session_end)
|
|
93
|
+
|
|
94
|
+
cron_expression = get_cron_trigger(data.get("cron"))
|
|
95
|
+
|
|
96
|
+
return cls(
|
|
97
|
+
min_after_session_end=min_after_session_end,
|
|
98
|
+
cron=cron_expression,
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class DeletionPolicy(PrivacyPolicy):
|
|
103
|
+
"""Class for configuring periodic deletion in the tracker store."""
|
|
104
|
+
|
|
105
|
+
type: str = "deletion"
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class AnonymizationPolicy(PrivacyPolicy):
|
|
109
|
+
"""Class for configuring periodic anonymization in the tracker store."""
|
|
110
|
+
|
|
111
|
+
type: str = "anonymization"
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class TrackerStoreSettings(BaseModel):
|
|
115
|
+
"""Class for configuring tracker store settings."""
|
|
116
|
+
|
|
117
|
+
deletion_policy: Optional[DeletionPolicy] = None
|
|
118
|
+
"""The deletion policy to be used."""
|
|
119
|
+
anonymization_policy: Optional[AnonymizationPolicy] = None
|
|
120
|
+
"""The anonymization policy to be used."""
|
|
121
|
+
|
|
122
|
+
@classmethod
|
|
123
|
+
def from_dict(cls, data: Dict[str, Any]) -> TrackerStoreSettings:
|
|
124
|
+
"""Create a TrackerStoreSettings object from parsed data."""
|
|
125
|
+
deletion_policy = data.get(DELETION_KEY)
|
|
126
|
+
anonymization_policy = data.get(ANONYMIZATION_KEY)
|
|
127
|
+
|
|
128
|
+
deletion_policy = (
|
|
129
|
+
DeletionPolicy.from_dict(deletion_policy) if deletion_policy else None
|
|
130
|
+
)
|
|
131
|
+
anonymization_policy = (
|
|
132
|
+
AnonymizationPolicy.from_dict(anonymization_policy)
|
|
133
|
+
if anonymization_policy
|
|
134
|
+
else None
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
validate_policies(deletion_policy, anonymization_policy)
|
|
138
|
+
|
|
139
|
+
return cls(
|
|
140
|
+
deletion_policy=deletion_policy,
|
|
141
|
+
anonymization_policy=anonymization_policy,
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
class PrivacyConfig(BaseModel):
|
|
146
|
+
"""Class for configuring PII management."""
|
|
147
|
+
|
|
148
|
+
anonymization_rules: Dict[str, AnonymizationMethod]
|
|
149
|
+
""""Mapping of slot names to rules for anonymizing sensitive information."""
|
|
150
|
+
tracker_store_settings: Optional[TrackerStoreSettings] = None
|
|
151
|
+
"""The tracker store settings to be used for periodic jobs
|
|
152
|
+
anonymizing and deleting conversation data in the tracker store."""
|
|
153
|
+
|
|
154
|
+
@classmethod
|
|
155
|
+
def from_dict(cls, data: Dict[str, Any]) -> PrivacyConfig:
|
|
156
|
+
"""Create a PrivacyConfig object from parsed privacy config."""
|
|
157
|
+
# Validate the data against the schema
|
|
158
|
+
validate_privacy_config(data)
|
|
159
|
+
|
|
160
|
+
anonymization_rules = {
|
|
161
|
+
rule[SLOT_KEY]: AnonymizationMethod.from_dict(
|
|
162
|
+
rule.get(ANONYMIZATION_KEY, {})
|
|
163
|
+
)
|
|
164
|
+
for rule in data.get("rules", [])
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
tracker_store_settings = data.get(TRACKER_STORE_SETTINGS, {})
|
|
168
|
+
tracker_store_settings = (
|
|
169
|
+
TrackerStoreSettings.from_dict(tracker_store_settings)
|
|
170
|
+
if tracker_store_settings
|
|
171
|
+
else None
|
|
172
|
+
)
|
|
173
|
+
return cls(
|
|
174
|
+
anonymization_rules=anonymization_rules,
|
|
175
|
+
tracker_store_settings=tracker_store_settings,
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def validate_privacy_config(data: Dict[str, Any]) -> None:
|
|
180
|
+
"""Validate the privacy configuration."""
|
|
181
|
+
import importlib_resources
|
|
182
|
+
|
|
183
|
+
schema_file = str(
|
|
184
|
+
importlib_resources.files(PACKAGE_NAME).joinpath(PRIVACY_CONFIG_SCHEMA)
|
|
185
|
+
)
|
|
186
|
+
schema_content = read_json_file(schema_file)
|
|
187
|
+
try:
|
|
188
|
+
validate_data_with_jsonschema(data, schema_content)
|
|
189
|
+
except YamlValidationException as exception:
|
|
190
|
+
validation_errors = (
|
|
191
|
+
[error.message for error in exception.validation_errors]
|
|
192
|
+
if exception.validation_errors
|
|
193
|
+
else []
|
|
194
|
+
)
|
|
195
|
+
exception_message = exception.message
|
|
196
|
+
structlogger.error(
|
|
197
|
+
"privacy_config.invalid_privacy_config",
|
|
198
|
+
validation_errors=validation_errors,
|
|
199
|
+
event_info=f"Invalid privacy config: {exception_message}. "
|
|
200
|
+
f"Please check the configuration file.",
|
|
201
|
+
)
|
|
202
|
+
sys.exit(1)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def get_cron_trigger(cron_expression: str) -> CronTrigger:
|
|
206
|
+
"""Validate the crontab expression."""
|
|
207
|
+
try:
|
|
208
|
+
cron = CronTrigger.from_crontab(cron_expression)
|
|
209
|
+
except Exception as exc:
|
|
210
|
+
structlogger.error(
|
|
211
|
+
"privacy_config.invalid_cron_expression",
|
|
212
|
+
cron=cron_expression,
|
|
213
|
+
)
|
|
214
|
+
raise RasaException from exc
|
|
215
|
+
|
|
216
|
+
return cron
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def validate_min_after_session_end(min_after_session_end: int) -> None:
|
|
220
|
+
"""Validate the minimum time after session end."""
|
|
221
|
+
try:
|
|
222
|
+
inactivity_period = int(
|
|
223
|
+
os.getenv(USER_CHAT_INACTIVITY_IN_MINUTES_ENV_VAR_NAME, "30")
|
|
224
|
+
)
|
|
225
|
+
except (ValueError, TypeError) as exc:
|
|
226
|
+
raise RasaException(
|
|
227
|
+
f"Invalid value for {USER_CHAT_INACTIVITY_IN_MINUTES_ENV_VAR_NAME} "
|
|
228
|
+
f"env var: {exc}."
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
if min_after_session_end < inactivity_period:
|
|
232
|
+
raise RasaException(
|
|
233
|
+
f"Minimum time in minutes after session end must be greater than "
|
|
234
|
+
f"{USER_CHAT_INACTIVITY_IN_MINUTES_ENV_VAR_NAME} env var value."
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def validate_policies(
|
|
239
|
+
deletion_policy: Optional[DeletionPolicy],
|
|
240
|
+
anonymization_policy: Optional[AnonymizationPolicy],
|
|
241
|
+
) -> None:
|
|
242
|
+
"""Validate the deletion and anonymization policies' configurations."""
|
|
243
|
+
if not deletion_policy or not anonymization_policy:
|
|
244
|
+
return None
|
|
245
|
+
|
|
246
|
+
if (
|
|
247
|
+
deletion_policy.min_after_session_end
|
|
248
|
+
<= anonymization_policy.min_after_session_end
|
|
249
|
+
):
|
|
250
|
+
raise RasaException(
|
|
251
|
+
"Minimum time in minutes after session end for deletion policy "
|
|
252
|
+
"must be greater than that of the anonymization policy."
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
if deletion_policy.cron.fields == anonymization_policy.cron.fields:
|
|
256
|
+
raise RasaException(
|
|
257
|
+
"Cron expressions for the deletion and anonymization policies "
|
|
258
|
+
"must be different."
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
return None
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def validate_sensitive_slots(sensitive_slots: List[str], domain: "Domain") -> None:
|
|
265
|
+
"""Validate the sensitive slots defined in the privacy config against the domain."""
|
|
266
|
+
all_slot_names = [slot.name for slot in domain.slots]
|
|
267
|
+
all_good = True
|
|
268
|
+
for sensitive_slot in sensitive_slots:
|
|
269
|
+
if sensitive_slot not in all_slot_names:
|
|
270
|
+
structlogger.error(
|
|
271
|
+
"privacy_config.invalid_sensitive_slot",
|
|
272
|
+
sensitive_slot=sensitive_slot,
|
|
273
|
+
event_info="Sensitive slot not found in the domain.",
|
|
274
|
+
)
|
|
275
|
+
all_good = False
|
|
276
|
+
|
|
277
|
+
if not all_good:
|
|
278
|
+
raise RasaException(
|
|
279
|
+
"Sensitive slots defined in the privacy config do not match "
|
|
280
|
+
"the slots defined in the domain. Please check the slot names."
|
|
281
|
+
)
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "object",
|
|
3
|
+
"required": [
|
|
4
|
+
"rules"
|
|
5
|
+
],
|
|
6
|
+
"properties": {
|
|
7
|
+
"rules": {
|
|
8
|
+
"type": "array",
|
|
9
|
+
"items": {
|
|
10
|
+
"type": "object",
|
|
11
|
+
"properties": {
|
|
12
|
+
"slot": {
|
|
13
|
+
"type": "string"
|
|
14
|
+
},
|
|
15
|
+
"anonymization": {
|
|
16
|
+
"type": "object",
|
|
17
|
+
"properties": {
|
|
18
|
+
"type": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"enum": [
|
|
21
|
+
"redact",
|
|
22
|
+
"mask"
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
"redaction_char": {
|
|
26
|
+
"type": "string",
|
|
27
|
+
"minLength": 1,
|
|
28
|
+
"maxLength": 1
|
|
29
|
+
},
|
|
30
|
+
"keep_left": {
|
|
31
|
+
"type": "number",
|
|
32
|
+
"exclusiveMinimum": 0
|
|
33
|
+
},
|
|
34
|
+
"keep_right": {
|
|
35
|
+
"type": "number",
|
|
36
|
+
"exclusiveMinimum": 0
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"required": [
|
|
40
|
+
"type"
|
|
41
|
+
]
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"required": [
|
|
45
|
+
"slot",
|
|
46
|
+
"anonymization"
|
|
47
|
+
],
|
|
48
|
+
"additionalProperties": false
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"tracker_store_settings": {
|
|
52
|
+
"type": "object",
|
|
53
|
+
"properties": {
|
|
54
|
+
"deletion": {
|
|
55
|
+
"$ref": "#/$defs/trigger_settings"
|
|
56
|
+
},
|
|
57
|
+
"anonymization": {
|
|
58
|
+
"$ref": "#/$defs/trigger_settings"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
"$defs": {
|
|
64
|
+
"trigger_settings": {
|
|
65
|
+
"type": "object",
|
|
66
|
+
"properties": {
|
|
67
|
+
"min_after_session_end": {
|
|
68
|
+
"type": "number",
|
|
69
|
+
"exclusiveMinimum": 0
|
|
70
|
+
},
|
|
71
|
+
"cron": {
|
|
72
|
+
"type": "string"
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
"additionalProperties": false,
|
|
76
|
+
"dependentRequired": {
|
|
77
|
+
"min_after_session_end": [
|
|
78
|
+
"cron"
|
|
79
|
+
],
|
|
80
|
+
"cron": [
|
|
81
|
+
"min_after_session_end"
|
|
82
|
+
]
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|