rasa-pro 3.14.0rc1__py3-none-any.whl → 3.14.0rc3__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/agents/protocol/a2a/a2a_agent.py +50 -42
- rasa/agents/utils.py +27 -5
- rasa/agents/validation.py +7 -9
- rasa/api.py +1 -2
- rasa/builder/copilot/constants.py +4 -1
- rasa/builder/copilot/copilot.py +191 -79
- rasa/builder/copilot/models.py +306 -116
- rasa/builder/copilot/prompts/copilot_system_prompt.jinja2 +33 -12
- rasa/builder/copilot/prompts/copilot_training_error_handler_prompt.jinja2 +53 -0
- rasa/builder/copilot/prompts/latest_user_message_context_prompt.jinja2 +59 -29
- rasa/builder/copilot/telemetry.py +8 -0
- rasa/builder/guardrails/policy_checker.py +1 -1
- rasa/builder/jobs.py +182 -12
- rasa/builder/models.py +12 -3
- rasa/builder/service.py +16 -2
- rasa/cli/dialogue_understanding_test.py +1 -0
- rasa/cli/e2e_test.py +1 -0
- rasa/cli/inspect.py +1 -0
- rasa/cli/project_templates/basic/tests/e2e_test_cases/without_stub/general/feedback.yml +46 -0
- rasa/cli/project_templates/basic/tests/e2e_test_cases/without_stub/general/goodbye.yml +9 -0
- rasa/cli/project_templates/basic/tests/e2e_test_cases/without_stub/general/help.yml +8 -0
- rasa/cli/project_templates/basic/tests/e2e_test_cases/without_stub/general/human_handoff.yml +41 -0
- rasa/cli/project_templates/basic/tests/e2e_test_cases/without_stub/general/patterns.yml +32 -0
- rasa/cli/project_templates/basic/tests/e2e_test_cases/without_stub/general/show_faqs.yml +8 -0
- rasa/cli/project_templates/finance/domain/general/help.yml +0 -0
- rasa/cli/project_templates/telco/data/network/flow_solve_internet_issue.yml +2 -2
- rasa/cli/project_templates/telco/domain/network/solve_internet_issue.yml +1 -2
- rasa/cli/project_templates/telco/tests/e2e_test_cases/with_stub/network/solve_internet_not_slow.yml +33 -0
- rasa/cli/project_templates/telco/tests/e2e_test_cases/with_stub/network/solve_internet_slow.yml +47 -0
- rasa/cli/project_templates/telco/tests/e2e_test_cases/without_stub/general/hello.yml +8 -0
- rasa/cli/run.py +1 -5
- rasa/cli/shell.py +1 -0
- rasa/cli/train.py +1 -0
- rasa/cli/validation/bot_config.py +7 -2
- rasa/core/available_agents.py +65 -55
- rasa/core/brokers/kafka.py +5 -1
- rasa/core/concurrent_lock_store.py +38 -21
- rasa/core/config/available_endpoints.py +0 -3
- rasa/core/config/configuration.py +36 -1
- rasa/core/constants.py +6 -0
- rasa/core/iam_credentials_providers/aws_iam_credentials_providers.py +69 -4
- rasa/core/iam_credentials_providers/credentials_provider_protocol.py +2 -1
- rasa/core/lock_store.py +4 -0
- rasa/core/policies/flows/agent_executor.py +16 -8
- rasa/core/redis_connection_factory.py +7 -2
- rasa/core/tracker_stores/redis_tracker_store.py +4 -0
- rasa/core/tracker_stores/sql_tracker_store.py +3 -1
- rasa/dialogue_understanding/commands/start_flow_command.py +10 -3
- rasa/dialogue_understanding/commands/utils.py +15 -4
- rasa/dialogue_understanding/generator/llm_based_command_generator.py +4 -2
- rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +4 -4
- rasa/dialogue_understanding/generator/single_step/search_ready_llm_command_generator.py +4 -4
- rasa/dialogue_understanding/generator/single_step/single_step_based_llm_command_generator.py +2 -2
- rasa/dialogue_understanding_test/du_test_runner.py +2 -2
- rasa/e2e_test/e2e_test_runner.py +2 -2
- rasa/shared/agents/auth/auth_strategy/oauth2_auth_strategy.py +10 -4
- rasa/shared/agents/auth/constants.py +1 -0
- rasa/shared/core/flows/steps/call.py +2 -2
- rasa/telemetry.py +3 -3
- rasa/validator.py +37 -0
- rasa/version.py +1 -1
- {rasa_pro-3.14.0rc1.dist-info → rasa_pro-3.14.0rc3.dist-info}/METADATA +14 -2
- {rasa_pro-3.14.0rc1.dist-info → rasa_pro-3.14.0rc3.dist-info}/RECORD +83 -73
- rasa/cli/project_templates/telco/tests/e2e_test_cases/network/solve_internet_issue.yml +0 -57
- /rasa/cli/project_templates/{finance/tests/e2e_test_cases → basic/tests/e2e_test_cases/without_stub}/general/hello.yml +0 -0
- /rasa/cli/project_templates/finance/tests/e2e_test_cases/{accounts → without_stub/accounts}/check_balance.yml +0 -0
- /rasa/cli/project_templates/finance/tests/e2e_test_cases/{accounts → without_stub/accounts}/download_statements.yml +0 -0
- /rasa/cli/project_templates/finance/tests/e2e_test_cases/{cards → without_stub/cards}/block_card.yml +0 -0
- /rasa/cli/project_templates/finance/tests/e2e_test_cases/{general → without_stub/general}/bot_challenge.yml +0 -0
- /rasa/cli/project_templates/finance/tests/e2e_test_cases/{general → without_stub/general}/feedback.yml +0 -0
- /rasa/cli/project_templates/finance/tests/e2e_test_cases/{general → without_stub/general}/goodbye.yml +0 -0
- /rasa/cli/project_templates/{telco/tests/e2e_test_cases → finance/tests/e2e_test_cases/without_stub}/general/hello.yml +0 -0
- /rasa/cli/project_templates/finance/tests/e2e_test_cases/{general → without_stub/general}/human_handoff.yml +0 -0
- /rasa/cli/project_templates/finance/tests/e2e_test_cases/{general → without_stub/general}/patterns.yml +0 -0
- /rasa/cli/project_templates/finance/tests/e2e_test_cases/{transfers → without_stub/transfers}/transfer_money.yml +0 -0
- /rasa/cli/project_templates/telco/tests/e2e_test_cases/{billing → without_stub/billing}/understand_bill.yml +0 -0
- /rasa/cli/project_templates/telco/tests/e2e_test_cases/{general → without_stub/general}/bot_challenge.yml +0 -0
- /rasa/cli/project_templates/telco/tests/e2e_test_cases/{general → without_stub/general}/feedback.yml +0 -0
- /rasa/cli/project_templates/telco/tests/e2e_test_cases/{general → without_stub/general}/goodbye.yml +0 -0
- /rasa/cli/project_templates/telco/tests/e2e_test_cases/{general → without_stub/general}/human_handoff.yml +0 -0
- /rasa/cli/project_templates/telco/tests/e2e_test_cases/{general → without_stub/general}/patterns.yml +0 -0
- {rasa_pro-3.14.0rc1.dist-info → rasa_pro-3.14.0rc3.dist-info}/NOTICE +0 -0
- {rasa_pro-3.14.0rc1.dist-info → rasa_pro-3.14.0rc3.dist-info}/WHEEL +0 -0
- {rasa_pro-3.14.0rc1.dist-info → rasa_pro-3.14.0rc3.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
test_cases:
|
|
2
|
+
- test_case: human handoff happy path
|
|
3
|
+
steps:
|
|
4
|
+
- user: "I would like to speak with a human"
|
|
5
|
+
assertions:
|
|
6
|
+
- flow_started: "human_handoff"
|
|
7
|
+
- bot_uttered:
|
|
8
|
+
utter_name: "utter_ask_confirm_human_handoff"
|
|
9
|
+
buttons:
|
|
10
|
+
- payload: "/SetSlots(confirm_human_handoff=Yes)"
|
|
11
|
+
title: "Yes"
|
|
12
|
+
- payload: "/SetSlots(confirm_human_handoff=No)"
|
|
13
|
+
title: "No"
|
|
14
|
+
- user: "/SetSlots(confirm_human_handoff=Yes)"
|
|
15
|
+
assertions:
|
|
16
|
+
- slot_was_set:
|
|
17
|
+
- name: "confirm_human_handoff"
|
|
18
|
+
value: "Yes"
|
|
19
|
+
- bot_uttered:
|
|
20
|
+
utter_name: "utter_transferring_to_human"
|
|
21
|
+
- action_executed: action_human_handoff
|
|
22
|
+
# ====================================================>
|
|
23
|
+
- test_case: human handoff unhappy path
|
|
24
|
+
steps:
|
|
25
|
+
- user: "I would like to speak with a human"
|
|
26
|
+
assertions:
|
|
27
|
+
- flow_started: "human_handoff"
|
|
28
|
+
- bot_uttered:
|
|
29
|
+
utter_name: "utter_ask_confirm_human_handoff"
|
|
30
|
+
buttons:
|
|
31
|
+
- payload: "/SetSlots(confirm_human_handoff=Yes)"
|
|
32
|
+
title: "Yes"
|
|
33
|
+
- payload: "/SetSlots(confirm_human_handoff=No)"
|
|
34
|
+
title: "No"
|
|
35
|
+
- user: "/SetSlots(confirm_human_handoff=No)"
|
|
36
|
+
assertions:
|
|
37
|
+
- slot_was_set:
|
|
38
|
+
- name: "confirm_human_handoff"
|
|
39
|
+
value: "No"
|
|
40
|
+
- bot_uttered:
|
|
41
|
+
utter_name: "utter_human_handoff_cancelled"
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
test_cases:
|
|
2
|
+
- test_case: test pattern session start
|
|
3
|
+
steps:
|
|
4
|
+
- user: "/session_start"
|
|
5
|
+
assertions:
|
|
6
|
+
- flow_started: "pattern_session_start"
|
|
7
|
+
- bot_uttered:
|
|
8
|
+
utter_name: "utter_welcome"
|
|
9
|
+
#
|
|
10
|
+
- test_case: test basic conversation flow
|
|
11
|
+
steps:
|
|
12
|
+
- user: "hello"
|
|
13
|
+
assertions:
|
|
14
|
+
- flow_started: "hello"
|
|
15
|
+
- bot_uttered:
|
|
16
|
+
utter_name: "utter_hello"
|
|
17
|
+
- user: "what can you help me with?"
|
|
18
|
+
assertions:
|
|
19
|
+
- flow_started: "help"
|
|
20
|
+
- bot_uttered:
|
|
21
|
+
utter_name: "utter_what_can_you_do"
|
|
22
|
+
- user: "show me faqs"
|
|
23
|
+
assertions:
|
|
24
|
+
- flow_started: "show_faqs"
|
|
25
|
+
- bot_uttered:
|
|
26
|
+
utter_name: "utter_top_faqs"
|
|
27
|
+
- user: "goodbye"
|
|
28
|
+
assertions:
|
|
29
|
+
- flow_started: "goodbye_flow"
|
|
30
|
+
- bot_uttered:
|
|
31
|
+
utter_name: "utter_goodbye"
|
|
32
|
+
- flow_started: "leave_feedback"
|
|
File without changes
|
|
@@ -69,5 +69,5 @@ flows:
|
|
|
69
69
|
#when it is False this means this flow will only be run if it is called by another flow, not the user.
|
|
70
70
|
description: run diagnostics to check the internet speed for the user
|
|
71
71
|
steps:
|
|
72
|
-
- action:
|
|
73
|
-
- action: actions_run_speed_test
|
|
72
|
+
- action: utter_communicate_run_diagnostics
|
|
73
|
+
- action: actions_run_speed_test
|
|
@@ -14,7 +14,7 @@ responses:
|
|
|
14
14
|
- text: |
|
|
15
15
|
I see.
|
|
16
16
|
Let me try to identify the root cause of the issue you are facing.
|
|
17
|
-
|
|
17
|
+
utter_communicate_run_diagnostics:
|
|
18
18
|
- text: |
|
|
19
19
|
I will run a network diagnostics for you 🛜
|
|
20
20
|
Wait a moment please...
|
|
@@ -72,4 +72,3 @@ responses:
|
|
|
72
72
|
actions:
|
|
73
73
|
- actions_run_speed_test
|
|
74
74
|
- action_sleep_few_sec
|
|
75
|
-
|
rasa/cli/project_templates/telco/tests/e2e_test_cases/with_stub/network/solve_internet_not_slow.yml
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
stub_custom_actions:
|
|
2
|
+
actions_run_speed_test:
|
|
3
|
+
events:
|
|
4
|
+
- event: slot
|
|
5
|
+
name: network_speed
|
|
6
|
+
value: 150.0
|
|
7
|
+
responses:
|
|
8
|
+
- text: "Thank you for waiting... ✅ "
|
|
9
|
+
action_sleep_few_sec:
|
|
10
|
+
events: []
|
|
11
|
+
responses: []
|
|
12
|
+
|
|
13
|
+
test_cases:
|
|
14
|
+
- test_case: Solving Internet issue not slow
|
|
15
|
+
steps:
|
|
16
|
+
- user: "Hey, my internet is very slow. What's going on?"
|
|
17
|
+
assertions:
|
|
18
|
+
- flow_started: "fix_internet_slow"
|
|
19
|
+
- bot_uttered:
|
|
20
|
+
utter_name: "utter_acknowledge_issue"
|
|
21
|
+
- action_executed: action_sleep_few_sec
|
|
22
|
+
- flow_started: "run_diagnostics"
|
|
23
|
+
- bot_uttered:
|
|
24
|
+
utter_name: "utter_speed_network_not_issue"
|
|
25
|
+
- action_executed: "actions_run_speed_test"
|
|
26
|
+
- bot_uttered:
|
|
27
|
+
utter_name: "utter_propose_other_solutions"
|
|
28
|
+
- action_executed: "action_sleep_few_sec"
|
|
29
|
+
- bot_uttered:
|
|
30
|
+
utter_name: "utter_ask_more_help_needed"
|
|
31
|
+
- user: "/SetSlots(more_help_needed=True)"
|
|
32
|
+
assertions:
|
|
33
|
+
- flow_started: "human_handoff"
|
rasa/cli/project_templates/telco/tests/e2e_test_cases/with_stub/network/solve_internet_slow.yml
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
stub_custom_actions:
|
|
2
|
+
actions_run_speed_test:
|
|
3
|
+
events:
|
|
4
|
+
- event: slot
|
|
5
|
+
name: network_speed
|
|
6
|
+
value: 50.0
|
|
7
|
+
responses:
|
|
8
|
+
- text: "Thank you for waiting... ✅ "
|
|
9
|
+
action_sleep_few_sec:
|
|
10
|
+
events: []
|
|
11
|
+
responses: []
|
|
12
|
+
|
|
13
|
+
test_cases:
|
|
14
|
+
- test_case: Solving Internet slow issue
|
|
15
|
+
steps:
|
|
16
|
+
- user: "Hey, my internet is very slow. What's going on?"
|
|
17
|
+
assertions:
|
|
18
|
+
- flow_started: "fix_internet_slow"
|
|
19
|
+
- bot_uttered:
|
|
20
|
+
utter_name: "utter_acknowledge_issue"
|
|
21
|
+
- action_executed: action_sleep_few_sec
|
|
22
|
+
- flow_started: "run_diagnostics"
|
|
23
|
+
- bot_uttered:
|
|
24
|
+
utter_name: "utter_communicate_run_diagnostics"
|
|
25
|
+
- action_executed: "actions_run_speed_test"
|
|
26
|
+
- bot_uttered:
|
|
27
|
+
utter_name: "utter_acknowledge_speed_test_not_good"
|
|
28
|
+
- action_executed: "action_sleep_few_sec"
|
|
29
|
+
- bot_uttered:
|
|
30
|
+
utter_name: "utter_propose_reboot_router_solution"
|
|
31
|
+
- action_executed: "action_sleep_few_sec"
|
|
32
|
+
- flow_started: "reboot_router"
|
|
33
|
+
- bot_uttered:
|
|
34
|
+
utter_name: "utter_explain_reboot_router"
|
|
35
|
+
- bot_uttered:
|
|
36
|
+
utter_name: "utter_ask_reboot_router"
|
|
37
|
+
buttons:
|
|
38
|
+
- title: "Done"
|
|
39
|
+
payload: "/SetSlots(reboot_router=True)"
|
|
40
|
+
- title: "Facing a problem"
|
|
41
|
+
payload: "/SetSlots(reboot_router=False)"
|
|
42
|
+
- user: "/SetSlots(reboot_router=False)"
|
|
43
|
+
assertions:
|
|
44
|
+
- slot_was_set:
|
|
45
|
+
- name: "reboot_router"
|
|
46
|
+
value: False
|
|
47
|
+
- flow_started: "human_handoff"
|
rasa/cli/run.py
CHANGED
|
@@ -88,7 +88,7 @@ def run(args: argparse.Namespace) -> None:
|
|
|
88
88
|
"""
|
|
89
89
|
Configuration.initialise_endpoints(
|
|
90
90
|
args.endpoints,
|
|
91
|
-
).initialise_credentials(
|
|
91
|
+
).initialise_sub_agents(args.sub_agents).initialise_credentials(
|
|
92
92
|
args.credentials,
|
|
93
93
|
)
|
|
94
94
|
|
|
@@ -108,7 +108,6 @@ def run(args: argparse.Namespace) -> None:
|
|
|
108
108
|
# configured
|
|
109
109
|
|
|
110
110
|
import rasa.model
|
|
111
|
-
from rasa.core.available_agents import AvailableAgents
|
|
112
111
|
|
|
113
112
|
# start server if remote storage is configured
|
|
114
113
|
if args.remote_storage is not None:
|
|
@@ -122,9 +121,6 @@ def run(args: argparse.Namespace) -> None:
|
|
|
122
121
|
rasa_run(**vars(args))
|
|
123
122
|
return
|
|
124
123
|
|
|
125
|
-
# load sub-agents
|
|
126
|
-
AvailableAgents.get_instance(args.sub_agents)
|
|
127
|
-
|
|
128
124
|
# start server if local model found
|
|
129
125
|
args.model = _validate_model_path(args.model, "model", DEFAULT_MODELS_PATH)
|
|
130
126
|
local_model_set = True
|
rasa/cli/shell.py
CHANGED
|
@@ -115,6 +115,7 @@ def shell(args: argparse.Namespace) -> None:
|
|
|
115
115
|
# it can be used safely throughout the codebase with
|
|
116
116
|
# `Configuration.get_instance().endpoints`
|
|
117
117
|
Configuration.initialise_endpoints(endpoints_path=Path(args.endpoints))
|
|
118
|
+
Configuration.initialise_sub_agents(args.sub_agents)
|
|
118
119
|
model = get_validated_path(args.model, "model", DEFAULT_MODELS_PATH)
|
|
119
120
|
|
|
120
121
|
try:
|
rasa/cli/train.py
CHANGED
|
@@ -128,6 +128,7 @@ def run_training(args: argparse.Namespace, can_exit: bool = False) -> Optional[T
|
|
|
128
128
|
# Validates and loads endpoints with proper endpoint file location
|
|
129
129
|
# TODO(Radovan): this should be probably be done in Configuration
|
|
130
130
|
_check_nlg_endpoint_validity(args.endpoints)
|
|
131
|
+
Configuration.initialise_sub_agents(args.sub_agents)
|
|
131
132
|
|
|
132
133
|
training_files = [
|
|
133
134
|
get_validated_path(f, "data", DEFAULT_DATA_PATH, none_is_valid=True)
|
|
@@ -5,6 +5,7 @@ from typing import TYPE_CHECKING, Optional
|
|
|
5
5
|
import structlog
|
|
6
6
|
|
|
7
7
|
from rasa import telemetry
|
|
8
|
+
from rasa.core.constants import DEFAULT_SUB_AGENTS
|
|
8
9
|
from rasa.exceptions import ValidationError
|
|
9
10
|
from rasa.shared.importers.importer import TrainingDataImporter
|
|
10
11
|
from rasa.shared.utils.common import display_research_study_prompt
|
|
@@ -194,8 +195,12 @@ def validate_files(
|
|
|
194
195
|
valid_translations = True
|
|
195
196
|
valid_CALM_slot_mappings = validator.validate_CALM_slot_mappings()
|
|
196
197
|
|
|
197
|
-
# Validate sub-agents
|
|
198
|
-
|
|
198
|
+
# Validate sub-agents
|
|
199
|
+
sub_agents_path = sub_agents or DEFAULT_SUB_AGENTS
|
|
200
|
+
valid_sub_agents = _validate_sub_agents(sub_agents_path)
|
|
201
|
+
|
|
202
|
+
if valid_sub_agents:
|
|
203
|
+
valid_sub_agents = validator.validate_agent_flow_conflicts(sub_agents_path)
|
|
199
204
|
|
|
200
205
|
all_good = (
|
|
201
206
|
valid_domain
|
rasa/core/available_agents.py
CHANGED
|
@@ -9,7 +9,7 @@ from pydantic import BaseModel, Field, model_validator
|
|
|
9
9
|
from ruamel import yaml as yaml
|
|
10
10
|
|
|
11
11
|
from rasa.exceptions import ValidationError
|
|
12
|
-
from rasa.utils.
|
|
12
|
+
from rasa.shared.utils.yaml import read_config_file
|
|
13
13
|
|
|
14
14
|
DEFAULT_AGENTS_CONFIG_FOLDER = "sub_agents"
|
|
15
15
|
|
|
@@ -89,48 +89,68 @@ class AgentConfig(BaseModel):
|
|
|
89
89
|
connections: Optional[AgentConnections] = None
|
|
90
90
|
|
|
91
91
|
|
|
92
|
-
class AvailableAgents
|
|
92
|
+
class AvailableAgents:
|
|
93
93
|
"""Collection of configured agents."""
|
|
94
94
|
|
|
95
|
-
_instance = None
|
|
96
|
-
|
|
97
95
|
def __init__(self, agents: Optional[Dict[str, AgentConfig]] = None) -> None:
|
|
98
96
|
"""Create an `AvailableAgents` object."""
|
|
99
|
-
self.agents = agents or {}
|
|
97
|
+
self.agents: Dict[str, AgentConfig] = agents or {}
|
|
100
98
|
|
|
101
99
|
@classmethod
|
|
102
|
-
def
|
|
100
|
+
def read_from_folder(cls, sub_agents_folder: str) -> AvailableAgents:
|
|
103
101
|
"""Read the different agents from the given folder."""
|
|
104
102
|
agents: Dict[str, AgentConfig] = {}
|
|
105
103
|
|
|
106
|
-
if not os.path.isdir(
|
|
107
|
-
if
|
|
104
|
+
if not os.path.isdir(sub_agents_folder):
|
|
105
|
+
if sub_agents_folder != DEFAULT_AGENTS_CONFIG_FOLDER:
|
|
108
106
|
# User explicitly specified a folder, it should exist
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
f"
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
f"exist or is not a directory."
|
|
107
|
+
raise ValidationError(
|
|
108
|
+
code="agent.sub_agents_folder_not_found",
|
|
109
|
+
event_info=f"The specified agents config folder "
|
|
110
|
+
f"'{sub_agents_folder}' does not exist or is not a "
|
|
111
|
+
f"directory.",
|
|
112
|
+
details={"folder": sub_agents_folder},
|
|
116
113
|
)
|
|
117
114
|
else:
|
|
118
115
|
# We are using the default folder, it may not be created yet
|
|
119
116
|
# Init with an empty agents in this case
|
|
120
117
|
structlogger.info(
|
|
121
|
-
f"Default agents config folder '{
|
|
122
|
-
f"Agent configurations won't be loaded."
|
|
118
|
+
f"Default agents config folder '{sub_agents_folder}' does not "
|
|
119
|
+
f"exist. Agent configurations won't be loaded."
|
|
123
120
|
)
|
|
124
121
|
return cls(agents)
|
|
125
122
|
|
|
126
123
|
# First, load all agent configs into a temporary list for validation
|
|
127
124
|
agent_configs: List[AgentConfig] = []
|
|
128
|
-
for agent_name in os.listdir(
|
|
129
|
-
|
|
125
|
+
for agent_name in os.listdir(sub_agents_folder):
|
|
126
|
+
agent_folder = os.path.join(sub_agents_folder, agent_name)
|
|
127
|
+
if not os.path.isdir(agent_folder):
|
|
128
|
+
raise ValidationError(
|
|
129
|
+
code="agent.invalid_directory_structure",
|
|
130
|
+
event_info=f"Invalid structure: '{agent_folder}' is not a folder. "
|
|
131
|
+
f"Each agent must be stored in its own folder inside "
|
|
132
|
+
f"'{sub_agents_folder}'. Expected structure: "
|
|
133
|
+
f"{sub_agents_folder}/<agent_name>/config.yml",
|
|
134
|
+
details={
|
|
135
|
+
"agent_name": agent_name,
|
|
136
|
+
"sub_agents_folder": sub_agents_folder,
|
|
137
|
+
},
|
|
138
|
+
)
|
|
139
|
+
config_path = os.path.join(agent_folder, "config.yml")
|
|
130
140
|
if not os.path.isfile(config_path):
|
|
131
|
-
|
|
141
|
+
raise ValidationError(
|
|
142
|
+
code="agent.missing_config_file",
|
|
143
|
+
event_info=f"Missing config file for agent '{agent_name}'. "
|
|
144
|
+
f"Expected file: '{config_path}'. "
|
|
145
|
+
f"Each agent folder must contain a 'config.yml' file.",
|
|
146
|
+
details={
|
|
147
|
+
"agent_name": agent_name,
|
|
148
|
+
"expected_config_file": config_path,
|
|
149
|
+
"sub_agents_folder": sub_agents_folder,
|
|
150
|
+
},
|
|
151
|
+
)
|
|
132
152
|
try:
|
|
133
|
-
agent_config = cls.
|
|
153
|
+
agent_config = cls._read_agent_config_file(config_path)
|
|
134
154
|
if not isinstance(agent_config, AgentConfig):
|
|
135
155
|
raise ValueError(f"Invalid agent config type for {agent_name}")
|
|
136
156
|
agent_configs.append(agent_config)
|
|
@@ -140,7 +160,7 @@ class AvailableAgents(metaclass=Singleton):
|
|
|
140
160
|
event_info=f"Failed to load agent '{agent_name}': {e}",
|
|
141
161
|
details={
|
|
142
162
|
"agent_name": agent_name,
|
|
143
|
-
"
|
|
163
|
+
"sub_agents_folder": sub_agents_folder,
|
|
144
164
|
"error": str(e),
|
|
145
165
|
},
|
|
146
166
|
)
|
|
@@ -153,27 +173,23 @@ class AvailableAgents(metaclass=Singleton):
|
|
|
153
173
|
for agent_config in agent_configs:
|
|
154
174
|
agents[agent_config.agent.name] = agent_config
|
|
155
175
|
|
|
176
|
+
structlogger.info(f"Loaded agent configs: {[k for k in agents.keys()]}")
|
|
156
177
|
return cls(agents)
|
|
157
178
|
|
|
158
179
|
@staticmethod
|
|
159
|
-
def
|
|
160
|
-
"""
|
|
180
|
+
def from_dict(data: Dict[str, Any]) -> AgentConfig:
|
|
181
|
+
"""Parse the agent config from raw data into Pydantic models.
|
|
161
182
|
|
|
162
183
|
Args:
|
|
163
|
-
|
|
184
|
+
data: Raw data from the config file as a dictionary.
|
|
164
185
|
|
|
165
186
|
Returns:
|
|
166
|
-
The parsed AgentConfig.
|
|
187
|
+
The parsed AgentConfig as a Pydantic model.
|
|
167
188
|
|
|
168
189
|
Raises:
|
|
169
|
-
yaml.YAMLError: If the YAML file is invalid.
|
|
170
190
|
ValueError: If the data structure is invalid for Pydantic models.
|
|
171
191
|
"""
|
|
172
|
-
|
|
173
|
-
data = yaml.safe_load(f)
|
|
174
|
-
|
|
175
|
-
# Create the agent config (this will trigger Pydantic validation)
|
|
176
|
-
agent_config = AgentConfig(
|
|
192
|
+
return AgentConfig(
|
|
177
193
|
agent=AgentInfo(**data.get("agent", {})),
|
|
178
194
|
configuration=AgentConfiguration(**data.get("configuration", {}))
|
|
179
195
|
if data.get("configuration")
|
|
@@ -183,24 +199,22 @@ class AvailableAgents(metaclass=Singleton):
|
|
|
183
199
|
else None,
|
|
184
200
|
)
|
|
185
201
|
|
|
186
|
-
return agent_config
|
|
187
|
-
|
|
188
202
|
@classmethod
|
|
189
|
-
def
|
|
190
|
-
|
|
191
|
-
) -> AvailableAgents:
|
|
192
|
-
"""Get the singleton instance of `AvailableAgents`."""
|
|
193
|
-
if cls._instance is None:
|
|
194
|
-
cls._instance = cls._read_agent_folder(agent_folder)
|
|
203
|
+
def _read_agent_config_file(cls, config_path: str) -> AgentConfig:
|
|
204
|
+
"""Read the agent config from a yaml file into Pydantic models.
|
|
195
205
|
|
|
196
|
-
|
|
206
|
+
Args:
|
|
207
|
+
config_path: Path to the config file.
|
|
197
208
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
209
|
+
Returns:
|
|
210
|
+
The parsed AgentConfig.
|
|
211
|
+
|
|
212
|
+
Raises:
|
|
213
|
+
yaml.YAMLError: If the YAML file is invalid.
|
|
214
|
+
ValidationError: If the data structure is invalid for Pydantic models.
|
|
215
|
+
"""
|
|
216
|
+
data = read_config_file(config_path)
|
|
217
|
+
return cls.from_dict(data)
|
|
204
218
|
|
|
205
219
|
def as_json_list(self) -> List[Dict[str, Any]]:
|
|
206
220
|
"""Convert the available agents to a JSON-serializable list."""
|
|
@@ -218,12 +232,8 @@ class AvailableAgents(metaclass=Singleton):
|
|
|
218
232
|
for agent_name, agent_config in self.agents.items()
|
|
219
233
|
]
|
|
220
234
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
instance = cls.get_instance()
|
|
224
|
-
return instance.agents.get(agent_id)
|
|
235
|
+
def get_agent_config(self, agent_id: str) -> Optional[AgentConfig]:
|
|
236
|
+
return self.agents.get(agent_id)
|
|
225
237
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
instance = cls.get_instance()
|
|
229
|
-
return len(instance.agents) > 0
|
|
238
|
+
def has_agents(self) -> bool:
|
|
239
|
+
return len(self.agents) > 0
|
rasa/core/brokers/kafka.py
CHANGED
|
@@ -12,6 +12,7 @@ import structlog
|
|
|
12
12
|
|
|
13
13
|
import rasa.shared.utils.common
|
|
14
14
|
from rasa.core.brokers.broker import EventBroker
|
|
15
|
+
from rasa.core.constants import KAFKA_SERVICE_NAME
|
|
15
16
|
from rasa.core.exceptions import KafkaProducerInitializationError
|
|
16
17
|
from rasa.core.iam_credentials_providers.credentials_provider_protocol import (
|
|
17
18
|
IAMCredentialsProviderInput,
|
|
@@ -99,7 +100,10 @@ class KafkaEventBroker(EventBroker):
|
|
|
99
100
|
self.queue_size = kwargs.get("queue_size")
|
|
100
101
|
self.ssl_check_hostname = "https" if ssl_check_hostname else None
|
|
101
102
|
self.iam_credentials_provider = create_iam_credentials_provider(
|
|
102
|
-
IAMCredentialsProviderInput(
|
|
103
|
+
IAMCredentialsProviderInput(
|
|
104
|
+
service_type=SupportedServiceType.EVENT_BROKER,
|
|
105
|
+
service_name=KAFKA_SERVICE_NAME,
|
|
106
|
+
)
|
|
103
107
|
)
|
|
104
108
|
|
|
105
109
|
# PII management attributes
|
|
@@ -6,6 +6,9 @@ from typing import Deque, Optional, Text
|
|
|
6
6
|
import structlog
|
|
7
7
|
from pydantic import ValidationError
|
|
8
8
|
|
|
9
|
+
from rasa.core.iam_credentials_providers.credentials_provider_protocol import (
|
|
10
|
+
SupportedServiceType,
|
|
11
|
+
)
|
|
9
12
|
from rasa.core.lock import Ticket, TicketLock
|
|
10
13
|
from rasa.core.lock_store import (
|
|
11
14
|
DEFAULT_SOCKET_TIMEOUT_IN_SECONDS,
|
|
@@ -108,6 +111,7 @@ class ConcurrentRedisLockStore(LockStore):
|
|
|
108
111
|
redis_config = RedisConfig(
|
|
109
112
|
host=host,
|
|
110
113
|
port=port,
|
|
114
|
+
service_type=SupportedServiceType.LOCK_STORE,
|
|
111
115
|
db=db,
|
|
112
116
|
username=username,
|
|
113
117
|
password=password,
|
|
@@ -150,32 +154,45 @@ class ConcurrentRedisLockStore(LockStore):
|
|
|
150
154
|
),
|
|
151
155
|
)
|
|
152
156
|
|
|
153
|
-
def
|
|
154
|
-
"""
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
157
|
+
def _scan_cluster_keys(self, pattern: Text) -> list:
|
|
158
|
+
"""Scan keys in cluster mode with proper cursor handling."""
|
|
159
|
+
keys = []
|
|
160
|
+
cursor = 0
|
|
161
|
+
|
|
162
|
+
while True:
|
|
163
|
+
try:
|
|
164
|
+
cursor, batch_keys = self.red.scan(cursor, match=pattern, count=100)
|
|
165
|
+
keys.extend(batch_keys)
|
|
166
|
+
|
|
167
|
+
if isinstance(cursor, dict):
|
|
168
|
+
# cursor is a dict mapping each node to its scan position. e.g
|
|
169
|
+
# {'127.0.0.1:7000': 0, '127.0.0.1:7001': 5, '127.0.0.1:7002': 0}
|
|
170
|
+
# A cursor value of 0 means that node has finished scanning
|
|
171
|
+
# When all nodes show 0, the entire cluster scan is complete
|
|
172
|
+
if all(v == 0 for v in cursor.values()):
|
|
173
|
+
break
|
|
174
|
+
else:
|
|
175
|
+
# if scan is complete
|
|
164
176
|
if cursor == 0:
|
|
165
177
|
break
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
)
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
# Standard and sentinel modes use KEYS
|
|
175
|
-
keys = self.red.keys(pattern)
|
|
178
|
+
|
|
179
|
+
except Exception as e:
|
|
180
|
+
structlogger.warning(
|
|
181
|
+
"concurrent_redis_lock_store._get_keys_by_pattern.scan_interrupted",
|
|
182
|
+
event_info=f"SCAN interrupted in cluster mode: {e}. "
|
|
183
|
+
f"Returning {len(keys)} keys found so far.",
|
|
184
|
+
)
|
|
185
|
+
break
|
|
176
186
|
|
|
177
187
|
return keys
|
|
178
188
|
|
|
189
|
+
def _get_keys_by_pattern(self, pattern: Text) -> list:
|
|
190
|
+
"""Get keys by pattern, using SCAN for cluster mode and KEYS for others."""
|
|
191
|
+
if self.deployment_mode == DeploymentMode.CLUSTER.value:
|
|
192
|
+
return self._scan_cluster_keys(pattern)
|
|
193
|
+
else:
|
|
194
|
+
return self.red.keys(pattern)
|
|
195
|
+
|
|
179
196
|
def issue_ticket(
|
|
180
197
|
self, conversation_id: Text, lock_lifetime: float = LOCK_LIFETIME
|
|
181
198
|
) -> int:
|
|
@@ -94,12 +94,9 @@ class MCPServerConfig(BaseModel):
|
|
|
94
94
|
class AvailableEndpoints:
|
|
95
95
|
"""Collection of configured endpoints."""
|
|
96
96
|
|
|
97
|
-
_instance = None
|
|
98
|
-
|
|
99
97
|
@classmethod
|
|
100
98
|
def read_endpoints(cls, endpoint_file: Path) -> AvailableEndpoints:
|
|
101
99
|
"""Read the different endpoints from a yaml file."""
|
|
102
|
-
|
|
103
100
|
nlg = read_endpoint_config(endpoint_file, endpoint_type="nlg")
|
|
104
101
|
nlu = read_endpoint_config(endpoint_file, endpoint_type="nlu")
|
|
105
102
|
action = read_endpoint_config(endpoint_file, endpoint_type="action_endpoint")
|