rasa-pro 3.14.0.dev20250731__py3-none-any.whl → 3.14.0.dev20250825__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.

Files changed (79) hide show
  1. rasa/core/channels/channel.py +4 -3
  2. rasa/core/channels/constants.py +3 -0
  3. rasa/core/channels/development_inspector.py +48 -15
  4. rasa/core/channels/inspector/dist/assets/{arc-0b11fe30.js → arc-1ddec37b.js} +1 -1
  5. rasa/core/channels/inspector/dist/assets/{blockDiagram-38ab4fdb-9eef30a7.js → blockDiagram-38ab4fdb-18af387c.js} +1 -1
  6. rasa/core/channels/inspector/dist/assets/{c4Diagram-3d4e48cf-03e94f28.js → c4Diagram-3d4e48cf-250127a3.js} +1 -1
  7. rasa/core/channels/inspector/dist/assets/channel-59f6d54b.js +1 -0
  8. rasa/core/channels/inspector/dist/assets/{classDiagram-70f12bd4-95c09eba.js → classDiagram-70f12bd4-c3388b34.js} +1 -1
  9. rasa/core/channels/inspector/dist/assets/{classDiagram-v2-f2320105-38e8446c.js → classDiagram-v2-f2320105-9c893a82.js} +1 -1
  10. rasa/core/channels/inspector/dist/assets/clone-26177ddb.js +1 -0
  11. rasa/core/channels/inspector/dist/assets/{createText-2e5e7dd3-57dc3038.js → createText-2e5e7dd3-c111213b.js} +1 -1
  12. rasa/core/channels/inspector/dist/assets/{edges-e0da2a9e-4bac0545.js → edges-e0da2a9e-812a729d.js} +1 -1
  13. rasa/core/channels/inspector/dist/assets/{erDiagram-9861fffd-81795c90.js → erDiagram-9861fffd-fd5051bc.js} +1 -1
  14. rasa/core/channels/inspector/dist/assets/{flowDb-956e92f1-89489ae6.js → flowDb-956e92f1-3287ac02.js} +1 -1
  15. rasa/core/channels/inspector/dist/assets/{flowDiagram-66a62f08-cd152627.js → flowDiagram-66a62f08-692fb0b2.js} +1 -1
  16. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-29c03f5a.js +1 -0
  17. rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-4a651766-3da369bc.js → flowchart-elk-definition-4a651766-008376f1.js} +1 -1
  18. rasa/core/channels/inspector/dist/assets/{ganttDiagram-c361ad54-85ec16f8.js → ganttDiagram-c361ad54-df330a69.js} +1 -1
  19. rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-72cf32ee-495bc140.js → gitGraphDiagram-72cf32ee-e03676fb.js} +1 -1
  20. rasa/core/channels/inspector/dist/assets/{graph-1ec4d266.js → graph-46fad2ba.js} +1 -1
  21. rasa/core/channels/inspector/dist/assets/{index-3862675e-0a0e97c9.js → index-3862675e-a484ac55.js} +1 -1
  22. rasa/core/channels/inspector/dist/assets/{index-c804b295.js → index-a003633f.js} +164 -164
  23. rasa/core/channels/inspector/dist/assets/{infoDiagram-f8f76790-4d54bcde.js → infoDiagram-f8f76790-3f9e6ec2.js} +1 -1
  24. rasa/core/channels/inspector/dist/assets/{journeyDiagram-49397b02-dc097114.js → journeyDiagram-49397b02-79f72383.js} +1 -1
  25. rasa/core/channels/inspector/dist/assets/{layout-1a08981e.js → layout-aad098e5.js} +1 -1
  26. rasa/core/channels/inspector/dist/assets/{line-95f7f1d3.js → line-219ab7ae.js} +1 -1
  27. rasa/core/channels/inspector/dist/assets/{linear-97e69543.js → linear-2cddbe62.js} +1 -1
  28. rasa/core/channels/inspector/dist/assets/{mindmap-definition-fc14e90a-8c71ff03.js → mindmap-definition-fc14e90a-1d41ed99.js} +1 -1
  29. rasa/core/channels/inspector/dist/assets/{pieDiagram-8a3498a8-f14c71c7.js → pieDiagram-8a3498a8-cc496ee8.js} +1 -1
  30. rasa/core/channels/inspector/dist/assets/{quadrantDiagram-120e2f19-f1d3c9ff.js → quadrantDiagram-120e2f19-84d32884.js} +1 -1
  31. rasa/core/channels/inspector/dist/assets/{requirementDiagram-deff3bca-bfa2412f.js → requirementDiagram-deff3bca-c0deb984.js} +1 -1
  32. rasa/core/channels/inspector/dist/assets/{sankeyDiagram-04a897e0-53f2c97b.js → sankeyDiagram-04a897e0-b9d7fd62.js} +1 -1
  33. rasa/core/channels/inspector/dist/assets/{sequenceDiagram-704730f1-319d7c0e.js → sequenceDiagram-704730f1-7d517565.js} +1 -1
  34. rasa/core/channels/inspector/dist/assets/{stateDiagram-587899a1-76a09418.js → stateDiagram-587899a1-98ef9b27.js} +1 -1
  35. rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-d93cdb3a-a67f15d4.js → stateDiagram-v2-d93cdb3a-cee70748.js} +1 -1
  36. rasa/core/channels/inspector/dist/assets/{styles-6aaf32cf-0654e7c3.js → styles-6aaf32cf-3f9d1c96.js} +1 -1
  37. rasa/core/channels/inspector/dist/assets/{styles-9a916d00-1394bb9d.js → styles-9a916d00-67471923.js} +1 -1
  38. rasa/core/channels/inspector/dist/assets/{styles-c10674c1-e4c5bdae.js → styles-c10674c1-bd093fb7.js} +1 -1
  39. rasa/core/channels/inspector/dist/assets/{svgDrawCommon-08f97a94-50957104.js → svgDrawCommon-08f97a94-675794e8.js} +1 -1
  40. rasa/core/channels/inspector/dist/assets/{timeline-definition-85554ec2-b0885a6a.js → timeline-definition-85554ec2-0ac67617.js} +1 -1
  41. rasa/core/channels/inspector/dist/assets/{xychartDiagram-e933f94c-79e6541a.js → xychartDiagram-e933f94c-c018dc37.js} +1 -1
  42. rasa/core/channels/inspector/dist/index.html +2 -2
  43. rasa/core/channels/inspector/index.html +1 -1
  44. rasa/core/channels/inspector/src/App.tsx +53 -7
  45. rasa/core/channels/inspector/src/components/Chat.tsx +3 -2
  46. rasa/core/channels/inspector/src/components/DiagramFlow.tsx +1 -1
  47. rasa/core/channels/inspector/src/components/LatencyDisplay.tsx +268 -0
  48. rasa/core/channels/inspector/src/components/LoadingSpinner.tsx +6 -2
  49. rasa/core/channels/inspector/src/helpers/audio/audiostream.ts +8 -3
  50. rasa/core/channels/inspector/src/types.ts +8 -0
  51. rasa/core/channels/socketio.py +212 -51
  52. rasa/core/channels/studio_chat.py +77 -31
  53. rasa/core/channels/voice_stream/audiocodes.py +2 -2
  54. rasa/core/channels/voice_stream/browser_audio.py +20 -3
  55. rasa/core/channels/voice_stream/call_state.py +13 -2
  56. rasa/core/channels/voice_stream/genesys.py +2 -2
  57. rasa/core/channels/voice_stream/jambonz.py +2 -2
  58. rasa/core/channels/voice_stream/twilio_media_streams.py +2 -2
  59. rasa/core/channels/voice_stream/voice_channel.py +88 -16
  60. rasa/core/nlg/contextual_response_rephraser.py +13 -2
  61. rasa/core/run.py +13 -3
  62. rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +1 -1
  63. rasa/dialogue_understanding/processor/command_processor.py +27 -11
  64. rasa/model_manager/model_api.py +3 -3
  65. rasa/model_manager/socket_bridge.py +21 -16
  66. rasa/shared/providers/_utils.py +60 -44
  67. rasa/shared/providers/embedding/default_litellm_embedding_client.py +2 -0
  68. rasa/shared/providers/llm/default_litellm_llm_client.py +2 -0
  69. rasa/studio/upload.py +7 -4
  70. rasa/studio/utils.py +33 -22
  71. rasa/version.py +1 -1
  72. {rasa_pro-3.14.0.dev20250731.dist-info → rasa_pro-3.14.0.dev20250825.dist-info}/METADATA +6 -6
  73. {rasa_pro-3.14.0.dev20250731.dist-info → rasa_pro-3.14.0.dev20250825.dist-info}/RECORD +76 -74
  74. rasa/core/channels/inspector/dist/assets/channel-51d02e9e.js +0 -1
  75. rasa/core/channels/inspector/dist/assets/clone-cc738fa6.js +0 -1
  76. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-0c716443.js +0 -1
  77. {rasa_pro-3.14.0.dev20250731.dist-info → rasa_pro-3.14.0.dev20250825.dist-info}/NOTICE +0 -0
  78. {rasa_pro-3.14.0.dev20250731.dist-info → rasa_pro-3.14.0.dev20250825.dist-info}/WHEEL +0 -0
  79. {rasa_pro-3.14.0.dev20250731.dist-info → rasa_pro-3.14.0.dev20250825.dist-info}/entry_points.txt +0 -0
@@ -24,6 +24,7 @@ from rasa.shared.constants import (
24
24
  )
25
25
  from rasa.shared.core.domain import KEY_RESPONSES_TEXT, Domain
26
26
  from rasa.shared.core.events import BotUttered, UserUttered
27
+ from rasa.shared.core.flows.constants import KEY_TRANSLATION
27
28
  from rasa.shared.core.trackers import DialogueStateTracker
28
29
  from rasa.shared.nlu.constants import (
29
30
  KEY_COMPONENT_NAME,
@@ -307,7 +308,12 @@ class ContextualResponseRephraser(
307
308
  Returns:
308
309
  The response with the rephrased text.
309
310
  """
310
- if not (response_text := response.get(KEY_RESPONSES_TEXT)):
311
+ translation_response = response.get(KEY_TRANSLATION) or {}
312
+ lang_code = getattr(tracker.current_language, "code", None)
313
+ response_text = translation_response.get(
314
+ lang_code, response.get(KEY_RESPONSES_TEXT)
315
+ )
316
+ if not response_text:
311
317
  return response
312
318
 
313
319
  prompt_template_text = self._template_for_response_rephrasing(response)
@@ -369,12 +375,17 @@ class ContextualResponseRephraser(
369
375
  return response
370
376
 
371
377
  updated_text = llm_response.choices[0]
378
+
379
+ if lang_code in translation_response:
380
+ response[KEY_TRANSLATION][lang_code] = updated_text
381
+ else:
382
+ response[KEY_RESPONSES_TEXT] = updated_text
383
+
372
384
  structlogger.debug(
373
385
  "nlg.rewrite.complete",
374
386
  response_text=response_text,
375
387
  updated_text=updated_text,
376
388
  )
377
- response[KEY_RESPONSES_TEXT] = updated_text
378
389
  return response
379
390
 
380
391
  def does_response_allow_rephrasing(self, template: Dict[Text, Any]) -> bool:
rasa/core/run.py CHANGED
@@ -42,10 +42,20 @@ from rasa.utils import licensing
42
42
  logger = logging.getLogger() # get the root logger
43
43
 
44
44
 
45
- def create_http_input_channels(
45
+ def create_input_channels(
46
46
  channel: Optional[Text], credentials_file: Optional[Text]
47
47
  ) -> List[InputChannel]:
48
- """Instantiate the chosen input channel."""
48
+ """Instantiate the chosen input channel.
49
+
50
+ Args:
51
+ channel (optional): The name of the specific input channel to create.
52
+ credentials_file: Path to the credentials file containing channel credentials.
53
+
54
+ Returns:
55
+ A list of instantiated input channels. If a specific channel is provided,
56
+ it returns a list with that single channel. If no channel is specified,
57
+ it returns a list of all channels defined in the credentials file.
58
+ """
49
59
  if credentials_file:
50
60
  all_credentials = read_config_file(credentials_file)
51
61
  else:
@@ -253,7 +263,7 @@ def serve_application(
253
263
  if not channel and not credentials:
254
264
  channel = "cmdline"
255
265
 
256
- input_channels = create_http_input_channels(channel, credentials)
266
+ input_channels = create_input_channels(channel, credentials)
257
267
 
258
268
  if inspect:
259
269
  logger.info("Starting development inspector.")
@@ -81,7 +81,7 @@ responses:
81
81
  rephrase: True
82
82
 
83
83
  utter_inform_hangup:
84
- - text: It seems you are not there anymore. I will hang up shortly.
84
+ - text: I haven’t heard from you, so I’ll end our conversation shortly.
85
85
  metadata:
86
86
  rephrase: True
87
87
 
@@ -398,19 +398,12 @@ def clean_up_commands(
398
398
  """
399
399
  domain = domain if domain else Domain.empty()
400
400
 
401
- # we consider all slots that were set in the tracker for potential corrections
402
- # in the correct_slot_command we will check if a slot should actually be
403
- # corrected
404
- slots_so_far = set(
405
- [event.key for event in tracker.events if isinstance(event, SlotSet)]
406
- )
407
-
408
401
  clean_commands: List[Command] = []
409
402
 
410
403
  for command in commands:
411
404
  if isinstance(command, SetSlotCommand):
412
405
  clean_commands = clean_up_slot_command(
413
- clean_commands, command, tracker, all_flows, slots_so_far
406
+ clean_commands, command, tracker, all_flows
414
407
  )
415
408
 
416
409
  elif isinstance(command, CancelFlowCommand) and contains_command(
@@ -501,6 +494,25 @@ def clean_up_commands(
501
494
  return clean_commands
502
495
 
503
496
 
497
+ def _get_slots_eligible_for_correction(tracker: DialogueStateTracker) -> Set[str]:
498
+ """Get all slots that are eligible for correction.
499
+
500
+ # We consider all slots, which are not None, that were set in the tracker
501
+ # eligible for correction.
502
+ # In the correct_slot_command we will check if a slot should actually be
503
+ # corrected.
504
+ """
505
+ # get all slots that were set in the tracker
506
+ slots_so_far = set(
507
+ [event.key for event in tracker.events if isinstance(event, SlotSet)]
508
+ )
509
+
510
+ # filter out slots that are set to None (None = empty value)
511
+ slots_so_far = {slot for slot in slots_so_far if tracker.get_slot(slot) is not None}
512
+
513
+ return slots_so_far
514
+
515
+
504
516
  def ensure_max_number_of_command_type(
505
517
  commands: List[Command], command_type: Type[Command], n: int
506
518
  ) -> List[Command]:
@@ -560,7 +572,6 @@ def clean_up_slot_command(
560
572
  command: SetSlotCommand,
561
573
  tracker: DialogueStateTracker,
562
574
  all_flows: FlowsList,
563
- slots_so_far: Set[str],
564
575
  ) -> List[Command]:
565
576
  """Clean up a slot command.
566
577
 
@@ -573,7 +584,6 @@ def clean_up_slot_command(
573
584
  command: The command to clean up.
574
585
  tracker: The dialogue state tracker.
575
586
  all_flows: All flows.
576
- slots_so_far: The slots that have been filled so far.
577
587
 
578
588
  Returns:
579
589
  The cleaned up commands.
@@ -642,7 +652,13 @@ def clean_up_slot_command(
642
652
  )
643
653
  return resulting_commands
644
654
 
645
- if command.name in slots_so_far and command.name != ROUTE_TO_CALM_SLOT:
655
+ # get all slots that were set in the tracker and are eligible for correction
656
+ slots_eligible_for_correction = _get_slots_eligible_for_correction(tracker)
657
+
658
+ if (
659
+ command.name in slots_eligible_for_correction
660
+ and command.name != ROUTE_TO_CALM_SLOT
661
+ ):
646
662
  current_collect_info = get_current_collect_step(stack, all_flows)
647
663
 
648
664
  if current_collect_info and current_collect_info.collect == command.name:
@@ -571,10 +571,10 @@ def external_blueprint() -> Blueprint:
571
571
  """Create a blueprint for the model manager API."""
572
572
  from rasa.core.channels.socketio import SocketBlueprint
573
573
 
574
- sio = AsyncServer(async_mode="sanic", cors_allowed_origins="*")
575
- bp = SocketBlueprint(sio, "", "model_api_external")
574
+ sio_server = AsyncServer(async_mode="sanic", cors_allowed_origins="*")
575
+ bp = SocketBlueprint(sio_server, "", "model_api_external")
576
576
 
577
- create_bridge_server(sio, running_bots)
577
+ create_bridge_server(sio_server, running_bots)
578
578
 
579
579
  @bp.get("/health")
580
580
  async def health(request: Request) -> response.HTTPResponse:
@@ -2,8 +2,7 @@ import json
2
2
  from typing import Any, Dict, Optional
3
3
 
4
4
  import structlog
5
- from socketio import AsyncServer
6
- from socketio.asyncio_client import AsyncClient
5
+ from socketio import AsyncClient, AsyncServer # type: ignore[attr-defined]
7
6
  from socketio.exceptions import ConnectionRefusedError
8
7
 
9
8
  from rasa.model_manager.runner_service import BotSession
@@ -20,7 +19,7 @@ socket_proxy_clients = {}
20
19
 
21
20
 
22
21
  async def socketio_websocket_traffic_wrapper(
23
- sio: AsyncServer,
22
+ sio_server: AsyncServer,
24
23
  running_bots: Dict[str, BotSession],
25
24
  sid: str,
26
25
  auth: Optional[Dict],
@@ -56,7 +55,9 @@ async def socketio_websocket_traffic_wrapper(
56
55
  structlogger.error("model_runner.bot_not_alive", deployment_id=deployment_id)
57
56
  raise ConnectionRefusedError("model_runner.bot_not_alive")
58
57
 
59
- client = await create_bridge_client(sio, bot.internal_url, sid, deployment_id)
58
+ client = await create_bridge_client(
59
+ sio_server, bot.internal_url, sid, deployment_id
60
+ )
60
61
 
61
62
  if client.sid is not None:
62
63
  structlogger.debug(
@@ -71,20 +72,24 @@ async def socketio_websocket_traffic_wrapper(
71
72
  raise ConnectionRefusedError("model_runner.bot_connection_failed")
72
73
 
73
74
 
74
- def create_bridge_server(sio: AsyncServer, running_bots: Dict[str, BotSession]) -> None:
75
+ def create_bridge_server(
76
+ sio_server: AsyncServer, running_bots: Dict[str, BotSession]
77
+ ) -> None:
75
78
  """Create handlers for the socket server side.
76
79
 
77
80
  Forwards messages coming from the user to the bot.
78
81
  """
79
82
 
80
- @sio.on("connect")
83
+ @sio_server.on("connect")
81
84
  async def socketio_websocket_traffic(
82
85
  sid: str, environ: Dict, auth: Optional[Dict]
83
86
  ) -> bool:
84
87
  """Bridge websockets between user chat socket and bot server."""
85
- return await socketio_websocket_traffic_wrapper(sio, running_bots, sid, auth)
88
+ return await socketio_websocket_traffic_wrapper(
89
+ sio_server, running_bots, sid, auth
90
+ )
86
91
 
87
- @sio.on("disconnect")
92
+ @sio_server.on("disconnect")
88
93
  async def disconnect(sid: str) -> None:
89
94
  """Disconnect the bot connection."""
90
95
  structlogger.debug("model_runner.bot_disconnect", sid=sid)
@@ -92,7 +97,7 @@ def create_bridge_server(sio: AsyncServer, running_bots: Dict[str, BotSession])
92
97
  await socket_proxy_clients[sid].disconnect()
93
98
  del socket_proxy_clients[sid]
94
99
 
95
- @sio.on("*")
100
+ @sio_server.on("*")
96
101
  async def handle_message(event: str, sid: str, data: Dict[str, Any]) -> None:
97
102
  """Bridge messages between user and bot.
98
103
 
@@ -109,7 +114,7 @@ def create_bridge_server(sio: AsyncServer, running_bots: Dict[str, BotSession])
109
114
 
110
115
 
111
116
  async def create_bridge_client(
112
- sio: AsyncServer, url: str, sid: str, deployment_id: str
117
+ sio_server: AsyncServer, url: str, sid: str, deployment_id: str
113
118
  ) -> AsyncClient:
114
119
  """Create a new socket bridge client.
115
120
 
@@ -124,36 +129,36 @@ async def create_bridge_client(
124
129
  structlogger.debug(
125
130
  "model_runner.bot_session_confirmed", deployment_id=deployment_id
126
131
  )
127
- await sio.emit("session_confirm", room=sid)
132
+ await sio_server.emit("session_confirm", room=sid)
128
133
 
129
134
  @client.event # type: ignore[misc]
130
135
  async def bot_message(data: Dict[str, Any]) -> None:
131
136
  structlogger.debug("model_runner.bot_message", deployment_id=deployment_id)
132
- await sio.emit("bot_message", data, room=sid)
137
+ await sio_server.emit("bot_message", data, room=sid)
133
138
 
134
139
  @client.event # type: ignore[misc]
135
140
  async def error(data: Dict[str, Any]) -> None:
136
141
  structlogger.debug(
137
142
  "model_runner.bot_error", deployment_id=deployment_id, data=data
138
143
  )
139
- await sio.emit("error", data, room=sid)
144
+ await sio_server.emit("error", data, room=sid)
140
145
 
141
146
  @client.event # type: ignore[misc]
142
147
  async def tracker(data: Dict[str, Any]) -> None:
143
- await sio.emit("tracker", json.loads(data), room=sid)
148
+ await sio_server.emit("tracker", json.loads(data), room=sid)
144
149
 
145
150
  @client.event # type: ignore[misc]
146
151
  async def disconnect() -> None:
147
152
  structlogger.debug(
148
153
  "model_runner.bot_connection_closed", deployment_id=deployment_id
149
154
  )
150
- await sio.emit("disconnect", room=sid)
155
+ await sio_server.emit("disconnect", room=sid)
151
156
 
152
157
  @client.event # type: ignore[misc]
153
158
  async def connect_error() -> None:
154
159
  structlogger.error(
155
160
  "model_runner.bot_connection_error", deployment_id=deployment_id
156
161
  )
157
- await sio.emit("disconnect", room=sid)
162
+ await sio_server.emit("disconnect", room=sid)
158
163
 
159
164
  return client
@@ -1,87 +1,103 @@
1
1
  from typing import Any, Dict, Optional
2
2
 
3
+ import boto3
3
4
  import structlog
4
- from litellm import validate_environment
5
+ from botocore.exceptions import BotoCoreError, ClientError
5
6
 
6
7
  from rasa.shared.constants import (
7
8
  API_BASE_CONFIG_KEY,
8
9
  API_VERSION_CONFIG_KEY,
9
10
  AWS_ACCESS_KEY_ID_CONFIG_KEY,
10
- AWS_ACCESS_KEY_ID_ENV_VAR,
11
+ AWS_BEDROCK_PROVIDER,
11
12
  AWS_REGION_NAME_CONFIG_KEY,
12
- AWS_REGION_NAME_ENV_VAR,
13
+ AWS_SAGEMAKER_CHAT_PROVIDER,
14
+ AWS_SAGEMAKER_PROVIDER,
13
15
  AWS_SECRET_ACCESS_KEY_CONFIG_KEY,
14
- AWS_SECRET_ACCESS_KEY_ENV_VAR,
15
16
  AWS_SESSION_TOKEN_CONFIG_KEY,
16
- AWS_SESSION_TOKEN_ENV_VAR,
17
17
  AZURE_API_BASE_ENV_VAR,
18
18
  AZURE_API_VERSION_ENV_VAR,
19
19
  DEPLOYMENT_CONFIG_KEY,
20
20
  )
21
21
  from rasa.shared.exceptions import ProviderClientValidationError
22
- from rasa.shared.providers.embedding._base_litellm_embedding_client import (
23
- _VALIDATE_ENVIRONMENT_MISSING_KEYS_KEY,
24
- )
22
+ from rasa.shared.utils.io import resolve_environment_variables
25
23
 
26
24
  structlogger = structlog.get_logger()
27
25
 
28
26
 
29
27
  def validate_aws_setup_for_litellm_clients(
30
- litellm_model_name: str, litellm_call_kwargs: dict, source_log: str
28
+ litellm_model_name: str, litellm_call_kwargs: Dict, source_log: str, provider: str
31
29
  ) -> None:
32
- """Validates the AWS setup for LiteLLM clients to ensure all required
33
- environment variables or corresponding call kwargs are set.
30
+ """Validates the AWS setup for LiteLLM clients to ensure credentials are set.
34
31
 
35
32
  Args:
36
33
  litellm_model_name (str): The name of the LiteLLM model being validated.
37
34
  litellm_call_kwargs (dict): Additional keyword arguments passed to the client,
38
35
  which may include configuration values for AWS credentials.
39
36
  source_log (str): The source log identifier for structured logging.
37
+ provider (str): The provider for which the validation is being performed.
40
38
 
41
39
  Raises:
42
40
  ProviderClientValidationError: If any required AWS environment variable
43
41
  or corresponding configuration key is missing.
44
42
  """
45
-
46
- # Mapping of environment variable names to their corresponding config keys
47
- envs_to_args = {
48
- AWS_ACCESS_KEY_ID_ENV_VAR: AWS_ACCESS_KEY_ID_CONFIG_KEY,
49
- AWS_SECRET_ACCESS_KEY_ENV_VAR: AWS_SECRET_ACCESS_KEY_CONFIG_KEY,
50
- AWS_REGION_NAME_ENV_VAR: AWS_REGION_NAME_CONFIG_KEY,
51
- AWS_SESSION_TOKEN_ENV_VAR: AWS_SESSION_TOKEN_CONFIG_KEY,
52
- }
53
-
54
- # Validate the environment setup for the model
55
- validation_info = validate_environment(litellm_model_name)
56
- missing_environment_variables = validation_info.get(
57
- _VALIDATE_ENVIRONMENT_MISSING_KEYS_KEY, []
43
+ # expand environment variables if referenced in the config
44
+ resolved_litellm_call_kwargs: Dict = resolve_environment_variables(
45
+ litellm_call_kwargs
46
+ ) # type: ignore[assignment]
47
+
48
+ # boto3 only accepts bedrock and sagemaker as valid clients
49
+ # therefore we need to convert the provider name if it is defined
50
+ # as sagemaker_chat
51
+ provider = (
52
+ AWS_SAGEMAKER_PROVIDER if provider == AWS_SAGEMAKER_CHAT_PROVIDER else provider
58
53
  )
59
- # Filter out missing environment variables that have been set trough arguments
60
- # in extra parameters
61
- missing_environment_variables = [
62
- missing_env_var
63
- for missing_env_var in missing_environment_variables
64
- if litellm_call_kwargs.get(envs_to_args.get(missing_env_var)) is None
65
- ]
66
54
 
67
- if missing_environment_variables:
68
- missing_environment_details = [
69
- (
70
- f"'{missing_env_var}' environment variable or "
71
- f"'{envs_to_args.get(missing_env_var)}' config key"
72
- )
73
- for missing_env_var in missing_environment_variables
55
+ # if the AWS credentials are defined in the endpoints yaml model config,
56
+ # either as referenced secret env vars or direct values, we need to pass them
57
+ # to the boto3 client to ensure that the client can connect to the AWS service.
58
+ additional_kwargs: Dict[str, Any] = {}
59
+ if AWS_ACCESS_KEY_ID_CONFIG_KEY in resolved_litellm_call_kwargs:
60
+ additional_kwargs[AWS_ACCESS_KEY_ID_CONFIG_KEY] = resolved_litellm_call_kwargs[
61
+ AWS_ACCESS_KEY_ID_CONFIG_KEY
74
62
  ]
63
+ if AWS_SECRET_ACCESS_KEY_CONFIG_KEY in resolved_litellm_call_kwargs:
64
+ additional_kwargs[AWS_SECRET_ACCESS_KEY_CONFIG_KEY] = (
65
+ resolved_litellm_call_kwargs[AWS_SECRET_ACCESS_KEY_CONFIG_KEY]
66
+ )
67
+ if AWS_SESSION_TOKEN_CONFIG_KEY in resolved_litellm_call_kwargs:
68
+ additional_kwargs[AWS_SESSION_TOKEN_CONFIG_KEY] = resolved_litellm_call_kwargs[
69
+ AWS_SESSION_TOKEN_CONFIG_KEY
70
+ ]
71
+ if AWS_REGION_NAME_CONFIG_KEY in resolved_litellm_call_kwargs:
72
+ additional_kwargs["region_name"] = resolved_litellm_call_kwargs[
73
+ AWS_REGION_NAME_CONFIG_KEY
74
+ ]
75
+
76
+ try:
77
+ # We are using the boto3 client because it can discover the AWS credentials
78
+ # from the environment variables, credentials file, or IAM roles.
79
+ # This is necessary to ensure that the client can connect to the AWS service.
80
+ aws_client = boto3.client(provider, **additional_kwargs)
81
+
82
+ # Using different method calls available to different AWS clients
83
+ # to test the connection
84
+ if provider == AWS_SAGEMAKER_PROVIDER:
85
+ aws_client.list_models()
86
+ elif provider == AWS_BEDROCK_PROVIDER:
87
+ aws_client.get_model_invocation_logging_configuration()
88
+
89
+ except (ClientError, BotoCoreError) as exc:
75
90
  event_info = (
76
- f"The following environment variables or configuration keys are "
77
- f"missing: "
78
- f"{', '.join(missing_environment_details)}. "
79
- f"These settings are required for API calls."
91
+ f"Failed to validate AWS setup for LiteLLM clients: {exc}. "
92
+ f"Ensure that you are using one of the available authentication methods:"
93
+ f"credentials file, environment variables, or IAM roles. "
94
+ f"Also, ensure that the AWS region is set correctly. "
80
95
  )
81
96
  structlogger.error(
82
- f"{source_log}.validate_aws_environment_variables",
97
+ f"{source_log}.validate_aws_credentials_for_litellm_clients",
83
98
  event_info=event_info,
84
- missing_environment_variables=missing_environment_variables,
99
+ exception=str(exc),
100
+ model_name=litellm_model_name,
85
101
  )
86
102
  raise ProviderClientValidationError(event_info)
87
103
 
@@ -37,6 +37,7 @@ class DefaultLiteLLMEmbeddingClient(_BaseLiteLLMEmbeddingClient):
37
37
 
38
38
  @classmethod
39
39
  def from_config(cls, config: Dict[str, Any]) -> "DefaultLiteLLMEmbeddingClient":
40
+ """Creates a DefaultLiteLLMEmbeddingClient instance from a config dict."""
40
41
  default_config = DefaultLiteLLMClientConfig.from_dict(config)
41
42
  return cls(
42
43
  model=default_config.model,
@@ -121,6 +122,7 @@ class DefaultLiteLLMEmbeddingClient(_BaseLiteLLMEmbeddingClient):
121
122
  self._litellm_model_name,
122
123
  self._litellm_extra_parameters,
123
124
  "default_litellm_embedding_client",
125
+ provider=self.provider,
124
126
  )
125
127
  else:
126
128
  super().validate_client_setup()
@@ -39,6 +39,7 @@ class DefaultLiteLLMClient(_BaseLiteLLMClient):
39
39
 
40
40
  @classmethod
41
41
  def from_config(cls, config: Dict[str, Any]) -> DefaultLiteLLMClient:
42
+ """Creates a DefaultLiteLLMClient instance from a configuration dictionary."""
42
43
  default_config = DefaultLiteLLMClientConfig.from_dict(config)
43
44
  return cls(
44
45
  model=default_config.model,
@@ -110,6 +111,7 @@ class DefaultLiteLLMClient(_BaseLiteLLMClient):
110
111
  self._litellm_model_name,
111
112
  self._litellm_extra_parameters,
112
113
  "default_litellm_llm_client",
114
+ provider=self.provider,
113
115
  )
114
116
  else:
115
117
  super().validate_client_setup()
rasa/studio/upload.py CHANGED
@@ -115,9 +115,10 @@ def run_validation(args: argparse.Namespace) -> None:
115
115
  """
116
116
  from rasa.validator import Validator
117
117
 
118
+ training_data_paths = args.data if isinstance(args.data, list) else [args.data]
118
119
  training_data_importer = TrainingDataImporter.load_from_dict(
119
120
  domain_path=args.domain,
120
- training_data_paths=[args.data],
121
+ training_data_paths=training_data_paths,
121
122
  config_path=args.config,
122
123
  expand_env_vars=False,
123
124
  )
@@ -263,8 +264,9 @@ def build_calm_import_parts(
263
264
  domain_from_files = importer.get_user_domain().as_dict()
264
265
  domain = extract_values(domain_from_files, DOMAIN_KEYS)
265
266
 
267
+ training_data_paths = data_path if isinstance(data_path, list) else [str(data_path)]
266
268
  flow_importer = FlowSyncImporter.load_from_dict(
267
- training_data_paths=[str(data_path)], expand_env_vars=False
269
+ training_data_paths=training_data_paths, expand_env_vars=False
268
270
  )
269
271
 
270
272
  flows = list(flow_importer.get_user_flows())
@@ -272,7 +274,7 @@ def build_calm_import_parts(
272
274
  flows = read_yaml(flows_yaml, expand_env_vars=False)
273
275
 
274
276
  nlu_importer = TrainingDataImporter.load_from_dict(
275
- training_data_paths=[str(data_path)], expand_env_vars=False
277
+ training_data_paths=training_data_paths, expand_env_vars=False
276
278
  )
277
279
  nlu_data = nlu_importer.get_nlu_data()
278
280
  nlu_examples = nlu_data.filter_training_examples(
@@ -349,9 +351,10 @@ def upload_nlu_assistant(
349
351
  "rasa.studio.upload.nlu_data_read",
350
352
  event_info="Found DM1 assistant data, parsing...",
351
353
  )
354
+ training_data_paths = args.data if isinstance(args.data, list) else [args.data]
352
355
  importer = TrainingDataImporter.load_from_dict(
353
356
  domain_path=args.domain,
354
- training_data_paths=[args.data],
357
+ training_data_paths=training_data_paths,
355
358
  config_path=args.config,
356
359
  expand_env_vars=False,
357
360
  )
rasa/studio/utils.py CHANGED
@@ -1,33 +1,44 @@
1
1
  import argparse
2
2
  from pathlib import Path
3
+ from typing import List
3
4
 
4
5
  import rasa.shared.utils.cli
5
- from rasa.shared.constants import (
6
- DEFAULT_CONFIG_PATH,
7
- DEFAULT_DATA_PATH,
8
- DEFAULT_ENDPOINTS_PATH,
9
- )
10
- from rasa.studio.constants import DOMAIN_FILENAME
6
+
7
+ DOMAIN_FILENAME = "domain.yml"
8
+ DEFAULT_CONFIG_PATH = "config.yml"
9
+ DEFAULT_ENDPOINTS_PATH = "endpoints.yml"
10
+ DEFAULT_DATA_PATH = "data"
11
11
 
12
12
 
13
13
  def validate_argument_paths(args: argparse.Namespace) -> None:
14
- """Validates the paths provided in the command line arguments.
14
+ """Validate every path passed via CLI arguments.
15
15
 
16
16
  Args:
17
- args: The command line arguments containing paths to validate.
17
+ args: CLI arguments containing paths to validate.
18
+
19
+ Raises:
20
+ rasa.shared.utils.cli.PrintErrorAndExit: If any path does not exist.
18
21
  """
22
+ invalid_paths: List[str] = []
23
+
24
+ def collect_invalid_paths(arg_name: str, default: str) -> None:
25
+ value = getattr(args, arg_name, None)
26
+ path_values = value if isinstance(value, list) else [value]
27
+ for path_value in path_values:
28
+ if not path_value or path_value == default:
29
+ continue
30
+
31
+ if not Path(path_value).resolve().exists():
32
+ invalid_paths.append(f"{arg_name}: '{path_value}'")
33
+
34
+ collect_invalid_paths("domain", DOMAIN_FILENAME)
35
+ collect_invalid_paths("config", DEFAULT_CONFIG_PATH)
36
+ collect_invalid_paths("endpoints", DEFAULT_ENDPOINTS_PATH)
37
+ collect_invalid_paths("data", DEFAULT_DATA_PATH)
19
38
 
20
- def validate_path(arg_name: str, default: str) -> None:
21
- path_value = getattr(args, arg_name, None)
22
- if path_value and path_value != default:
23
- resolved_path = Path(path_value).resolve()
24
- if not resolved_path.exists():
25
- rasa.shared.utils.cli.print_error_and_exit(
26
- f"{arg_name.capitalize()} file or directory "
27
- f"'{path_value}' does not exist."
28
- )
29
-
30
- validate_path("domain", DOMAIN_FILENAME)
31
- validate_path("config", DEFAULT_CONFIG_PATH)
32
- validate_path("endpoints", DEFAULT_ENDPOINTS_PATH)
33
- validate_path("data", DEFAULT_DATA_PATH)
39
+ if invalid_paths:
40
+ message = (
41
+ "The following files or directories do not exist:\n - "
42
+ + "\n - ".join(invalid_paths)
43
+ )
44
+ rasa.shared.utils.cli.print_error_and_exit(message)
rasa/version.py CHANGED
@@ -1,3 +1,3 @@
1
1
  # this file will automatically be changed,
2
2
  # do not add anything but the version number here!
3
- __version__ = "3.14.0.dev20250731"
3
+ __version__ = "3.14.0.dev20250825"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: rasa-pro
3
- Version: 3.14.0.dev20250731
3
+ Version: 3.14.0.dev20250825
4
4
  Summary: State-of-the-art open-core Conversational AI framework for Enterprises that natively leverages generative AI for effortless assistant development.
5
5
  Keywords: nlp,machine-learning,machine-learning-library,bot,bots,botkit,rasa conversational-agents,conversational-ai,chatbot,chatbot-framework,bot-framework
6
6
  Author: Rasa Technologies GmbH
@@ -23,7 +23,7 @@ Provides-Extra: spacy
23
23
  Provides-Extra: transformers
24
24
  Requires-Dist: CacheControl (>=0.14.2,<0.15.0)
25
25
  Requires-Dist: PyJWT[crypto] (>=2.8.0,<3.0.0)
26
- Requires-Dist: SQLAlchemy (>=2.0.41,<2.1.0)
26
+ Requires-Dist: SQLAlchemy (>=2.0.42,<2.1.0)
27
27
  Requires-Dist: absl-py (>=2.0,<2.1)
28
28
  Requires-Dist: aio-pika (>=8.2.3,<9.4.4)
29
29
  Requires-Dist: aiogram (>=3.15,<3.16)
@@ -38,7 +38,7 @@ Requires-Dist: colorama (>=0.4.6,<0.5.0) ; sys_platform == "win32"
38
38
  Requires-Dist: colorclass (>=2.2,<2.3)
39
39
  Requires-Dist: coloredlogs (>=15,<16)
40
40
  Requires-Dist: colorhash (>=2.0,<2.1.0)
41
- Requires-Dist: confluent-kafka (>=2.10.0,<3.0.0)
41
+ Requires-Dist: confluent-kafka (>=2.11.0,<3.0.0)
42
42
  Requires-Dist: cryptography (>=44.0.1)
43
43
  Requires-Dist: cvg-python-sdk (>=0.5.1,<0.6.0)
44
44
  Requires-Dist: dask (>=2024.8.0,<2024.9.0)
@@ -93,9 +93,9 @@ Requires-Dist: python-dateutil (>=2.8.2,<2.9.0)
93
93
  Requires-Dist: python-dotenv (>=1.0.1,<2.0.0)
94
94
  Requires-Dist: python-engineio (>=4.12.2,<4.13.0)
95
95
  Requires-Dist: python-keycloak (>=3.12.0,<4.0.0)
96
- Requires-Dist: python-socketio (>=5.8,<6)
96
+ Requires-Dist: python-socketio (>=5.13,<6)
97
97
  Requires-Dist: pytz (>=2022.7.1,<2023.0)
98
- Requires-Dist: pyyaml (>=6.0)
98
+ Requires-Dist: pyyaml (>=6.0.2,<6.1.0)
99
99
  Requires-Dist: qdrant-client (>=1.9.1,<1.10.0)
100
100
  Requires-Dist: questionary (>=1.10.0,<2.1.0)
101
101
  Requires-Dist: randomname (>=0.2.1,<0.3.0)
@@ -120,7 +120,7 @@ Requires-Dist: sklearn-crfsuite (>=0.5.0,<0.6.0)
120
120
  Requires-Dist: skops (>=0.11.0,<0.12.0)
121
121
  Requires-Dist: slack-sdk (>=3.27.1,<3.28.0)
122
122
  Requires-Dist: spacy (>=3.5.4,<4.0.0) ; extra == "spacy" or extra == "full"
123
- Requires-Dist: structlog (>=23.1.0,<23.2.0)
123
+ Requires-Dist: structlog (>=25.4.0,<25.5.0)
124
124
  Requires-Dist: structlog-sentry (>=2.0.3,<2.1.0)
125
125
  Requires-Dist: tarsafe (>=0.0.5,<0.0.6)
126
126
  Requires-Dist: tenacity (>=8.4.1,<8.5.0)