rasa-pro 3.14.0.dev20250818__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.
- rasa/core/channels/channel.py +4 -3
- rasa/core/channels/constants.py +3 -0
- rasa/core/channels/development_inspector.py +1 -1
- rasa/core/channels/socketio.py +212 -51
- rasa/core/channels/studio_chat.py +18 -16
- rasa/core/channels/voice_stream/voice_channel.py +5 -3
- rasa/core/run.py +13 -3
- rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +1 -1
- rasa/model_manager/model_api.py +3 -3
- rasa/model_manager/socket_bridge.py +20 -14
- rasa/shared/providers/_utils.py +60 -44
- rasa/shared/providers/embedding/default_litellm_embedding_client.py +2 -0
- rasa/shared/providers/llm/default_litellm_llm_client.py +2 -0
- rasa/version.py +1 -1
- {rasa_pro-3.14.0.dev20250818.dist-info → rasa_pro-3.14.0.dev20250825.dist-info}/METADATA +1 -1
- {rasa_pro-3.14.0.dev20250818.dist-info → rasa_pro-3.14.0.dev20250825.dist-info}/RECORD +19 -18
- {rasa_pro-3.14.0.dev20250818.dist-info → rasa_pro-3.14.0.dev20250825.dist-info}/NOTICE +0 -0
- {rasa_pro-3.14.0.dev20250818.dist-info → rasa_pro-3.14.0.dev20250825.dist-info}/WHEEL +0 -0
- {rasa_pro-3.14.0.dev20250818.dist-info → rasa_pro-3.14.0.dev20250825.dist-info}/entry_points.txt +0 -0
rasa/core/channels/channel.py
CHANGED
|
@@ -102,6 +102,9 @@ class UserMessage:
|
|
|
102
102
|
return f"{self.__class__.__name__}({self.text})"
|
|
103
103
|
|
|
104
104
|
|
|
105
|
+
OnNewMessageType = Callable[[UserMessage], Awaitable[Any]]
|
|
106
|
+
|
|
107
|
+
|
|
105
108
|
def register(
|
|
106
109
|
input_channels: List[InputChannel], app: Sanic, route: Optional[Text]
|
|
107
110
|
) -> None:
|
|
@@ -135,9 +138,7 @@ class InputChannel:
|
|
|
135
138
|
def url_prefix(self) -> Text:
|
|
136
139
|
return self.name()
|
|
137
140
|
|
|
138
|
-
def blueprint(
|
|
139
|
-
self, on_new_message: Callable[[UserMessage], Awaitable[Any]]
|
|
140
|
-
) -> Blueprint:
|
|
141
|
+
def blueprint(self, on_new_message: OnNewMessageType) -> Blueprint:
|
|
141
142
|
"""Defines a Sanic blueprint.
|
|
142
143
|
|
|
143
144
|
The blueprint will be attached to a running sanic server and handle
|
|
@@ -195,7 +195,7 @@ class DevelopmentInspectProxy(InputChannel):
|
|
|
195
195
|
self, on_new_message: Callable[[UserMessage], Awaitable[Any]]
|
|
196
196
|
) -> "Blueprint":
|
|
197
197
|
"""Defines a Sanic blueprint."""
|
|
198
|
-
self.
|
|
198
|
+
self.sio_server = AsyncServer(async_mode="sanic", cors_allowed_origins=[])
|
|
199
199
|
underlying_webhook: Blueprint = self.underlying.blueprint(
|
|
200
200
|
partial(self.on_message_proxy, on_new_message)
|
|
201
201
|
)
|
rasa/core/channels/socketio.py
CHANGED
|
@@ -1,34 +1,54 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
1
4
|
import inspect
|
|
2
5
|
import json
|
|
3
|
-
import logging
|
|
4
6
|
import uuid
|
|
7
|
+
from asyncio import Task
|
|
8
|
+
from dataclasses import dataclass
|
|
5
9
|
from typing import Any, Awaitable, Callable, Dict, Iterable, List, Optional, Text
|
|
6
10
|
|
|
11
|
+
import structlog
|
|
7
12
|
from sanic import Blueprint, Sanic, response
|
|
8
13
|
from sanic.request import Request
|
|
9
14
|
from sanic.response import HTTPResponse
|
|
10
15
|
from socketio import AsyncServer
|
|
11
16
|
|
|
12
|
-
import rasa.core.channels.channel
|
|
13
17
|
import rasa.shared.utils.io
|
|
14
|
-
from rasa.core.channels.channel import
|
|
18
|
+
from rasa.core.channels.channel import (
|
|
19
|
+
InputChannel,
|
|
20
|
+
OnNewMessageType,
|
|
21
|
+
OutputChannel,
|
|
22
|
+
UserMessage,
|
|
23
|
+
)
|
|
24
|
+
from rasa.core.channels.constants import USER_CONVERSATION_SILENCE_TIMEOUT
|
|
25
|
+
from rasa.shared.core.constants import SILENCE_TIMEOUT_SLOT
|
|
26
|
+
|
|
27
|
+
logger = structlog.getLogger(__name__)
|
|
28
|
+
|
|
15
29
|
|
|
16
|
-
|
|
30
|
+
@dataclass
|
|
31
|
+
class SilenceTimeout:
|
|
32
|
+
sender_id: str
|
|
33
|
+
timeout: float
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
UserSilenceHandlerType = Callable[[SilenceTimeout], Awaitable[Any]]
|
|
17
37
|
|
|
18
38
|
|
|
19
39
|
class SocketBlueprint(Blueprint):
|
|
20
40
|
"""Blueprint for socketio connections."""
|
|
21
41
|
|
|
22
42
|
def __init__(
|
|
23
|
-
self,
|
|
43
|
+
self, sio_server: AsyncServer, socketio_path: Text, *args: Any, **kwargs: Any
|
|
24
44
|
) -> None:
|
|
25
|
-
"""Creates a :class:`sanic.Blueprint` for routing socketio
|
|
45
|
+
"""Creates a :class:`sanic.Blueprint` for routing socketio connections.
|
|
26
46
|
|
|
27
|
-
:param
|
|
47
|
+
:param sio_server: Instance of :class:`socketio.AsyncServer` class
|
|
28
48
|
:param socketio_path: string indicating the route to accept requests on.
|
|
29
49
|
"""
|
|
30
50
|
super().__init__(*args, **kwargs)
|
|
31
|
-
self.ctx.
|
|
51
|
+
self.ctx.sio_server = sio_server
|
|
32
52
|
self.ctx.socketio_path = socketio_path
|
|
33
53
|
|
|
34
54
|
def register(self, app: Sanic, options: Dict[Text, Any]) -> None:
|
|
@@ -42,7 +62,7 @@ class SocketBlueprint(Blueprint):
|
|
|
42
62
|
path = self.ctx.socketio_path
|
|
43
63
|
else:
|
|
44
64
|
path = options.get("url_prefix", "/socket.io")
|
|
45
|
-
self.ctx.
|
|
65
|
+
self.ctx.sio_server.attach(app, path)
|
|
46
66
|
super().register(app, options)
|
|
47
67
|
|
|
48
68
|
|
|
@@ -51,14 +71,45 @@ class SocketIOOutput(OutputChannel):
|
|
|
51
71
|
def name(cls) -> Text:
|
|
52
72
|
return "socketio"
|
|
53
73
|
|
|
54
|
-
def __init__(
|
|
74
|
+
def __init__(
|
|
75
|
+
self,
|
|
76
|
+
input_channel: SocketIOInput,
|
|
77
|
+
sio_server: AsyncServer,
|
|
78
|
+
bot_message_evt: Text,
|
|
79
|
+
) -> None:
|
|
55
80
|
super().__init__()
|
|
56
|
-
self.
|
|
81
|
+
self._input_channel = input_channel
|
|
82
|
+
self.sio_server = sio_server
|
|
57
83
|
self.bot_message_evt = bot_message_evt
|
|
58
84
|
|
|
59
85
|
async def _send_message(self, socket_id: Text, response: Any) -> None:
|
|
60
86
|
"""Sends a message to the recipient using the bot event."""
|
|
61
|
-
await self.
|
|
87
|
+
await self.sio_server.emit(self.bot_message_evt, response, room=socket_id)
|
|
88
|
+
if self.tracker_state and self._input_channel.enable_silence_timeout:
|
|
89
|
+
silence_timeout = self.tracker_state["slots"][SILENCE_TIMEOUT_SLOT]
|
|
90
|
+
|
|
91
|
+
logger.debug(
|
|
92
|
+
"socketio_channel.silence_timeout_updated",
|
|
93
|
+
sender_id=socket_id,
|
|
94
|
+
silence_timeout=silence_timeout,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
silence_timeout_counts = self.tracker_state["slots"][
|
|
98
|
+
"consecutive_silence_timeouts"
|
|
99
|
+
]
|
|
100
|
+
|
|
101
|
+
logger.debug(
|
|
102
|
+
"socketio_channel.consecutive_silence_timeouts_updated",
|
|
103
|
+
sender_id=socket_id,
|
|
104
|
+
silence_timeout_counts=silence_timeout_counts,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
self._input_channel.reset_silence_timeout(
|
|
108
|
+
SilenceTimeout(
|
|
109
|
+
sender_id=socket_id,
|
|
110
|
+
timeout=silence_timeout,
|
|
111
|
+
),
|
|
112
|
+
)
|
|
62
113
|
|
|
63
114
|
async def send_text_message(
|
|
64
115
|
self, recipient_id: Text, text: Text, **kwargs: Any
|
|
@@ -123,9 +174,11 @@ class SocketIOOutput(OutputChannel):
|
|
|
123
174
|
"""Sends custom json to the output."""
|
|
124
175
|
if "data" in json_message:
|
|
125
176
|
json_message.setdefault("room", recipient_id)
|
|
126
|
-
await self.
|
|
177
|
+
await self.sio_server.emit(self.bot_message_evt, **json_message)
|
|
127
178
|
else:
|
|
128
|
-
await self.
|
|
179
|
+
await self.sio_server.emit(
|
|
180
|
+
self.bot_message_evt, json_message, room=recipient_id
|
|
181
|
+
)
|
|
129
182
|
|
|
130
183
|
async def send_attachment( # type: ignore[override]
|
|
131
184
|
self, recipient_id: Text, attachment: Dict[Text, Any], **kwargs: Any
|
|
@@ -133,26 +186,38 @@ class SocketIOOutput(OutputChannel):
|
|
|
133
186
|
"""Sends an attachment to the user."""
|
|
134
187
|
await self._send_message(recipient_id, {"attachment": attachment})
|
|
135
188
|
|
|
189
|
+
async def hangup(self, recipient_id: Text, **kwargs: Any) -> None:
|
|
190
|
+
"""Hangs up the call for the given sender."""
|
|
191
|
+
# This method is not applicable for socket.io, but we implement it
|
|
192
|
+
# to satisfy the OutputChannel interface.
|
|
193
|
+
logger.debug(
|
|
194
|
+
"socketio_channel.output.hangup",
|
|
195
|
+
message=f"Hanging up call for user {recipient_id}.",
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
self._input_channel.disconnect(recipient_id)
|
|
199
|
+
|
|
136
200
|
|
|
137
201
|
class SocketIOInput(InputChannel):
|
|
138
202
|
"""A socket.io input channel."""
|
|
139
203
|
|
|
140
204
|
@classmethod
|
|
141
|
-
def name(cls) ->
|
|
205
|
+
def name(cls) -> str:
|
|
142
206
|
return "socketio"
|
|
143
207
|
|
|
144
208
|
@classmethod
|
|
145
209
|
def from_credentials(cls, credentials: Optional[Dict[Text, Any]]) -> InputChannel:
|
|
146
210
|
credentials = credentials or {}
|
|
147
211
|
return cls(
|
|
148
|
-
credentials.get("user_message_evt", "user_uttered"),
|
|
149
|
-
credentials.get("bot_message_evt", "bot_uttered"),
|
|
150
|
-
credentials.get("namespace"),
|
|
151
|
-
credentials.get("session_persistence", False),
|
|
152
|
-
credentials.get("socketio_path", "/socket.io"),
|
|
153
|
-
credentials.get("jwt_key"),
|
|
154
|
-
credentials.get("jwt_method", "HS256"),
|
|
155
|
-
credentials.get("metadata_key", "metadata"),
|
|
212
|
+
user_message_evt=credentials.get("user_message_evt", "user_uttered"),
|
|
213
|
+
bot_message_evt=credentials.get("bot_message_evt", "bot_uttered"),
|
|
214
|
+
namespace=credentials.get("namespace"),
|
|
215
|
+
session_persistence=credentials.get("session_persistence", False),
|
|
216
|
+
socketio_path=credentials.get("socketio_path", "/socket.io"),
|
|
217
|
+
jwt_key=credentials.get("jwt_key"),
|
|
218
|
+
jwt_method=credentials.get("jwt_method", "HS256"),
|
|
219
|
+
metadata_key=credentials.get("metadata_key", "metadata"),
|
|
220
|
+
enable_silence_timeout=credentials.get("enable_silence_timeout", False),
|
|
156
221
|
)
|
|
157
222
|
|
|
158
223
|
def __init__(
|
|
@@ -165,6 +230,7 @@ class SocketIOInput(InputChannel):
|
|
|
165
230
|
jwt_key: Optional[Text] = None,
|
|
166
231
|
jwt_method: Optional[Text] = "HS256",
|
|
167
232
|
metadata_key: Optional[Text] = "metadata",
|
|
233
|
+
enable_silence_timeout: bool = False,
|
|
168
234
|
):
|
|
169
235
|
"""Creates a ``SocketIOInput`` object."""
|
|
170
236
|
self.bot_message_evt = bot_message_evt
|
|
@@ -172,15 +238,18 @@ class SocketIOInput(InputChannel):
|
|
|
172
238
|
self.user_message_evt = user_message_evt
|
|
173
239
|
self.namespace = namespace
|
|
174
240
|
self.socketio_path = socketio_path
|
|
175
|
-
self.
|
|
241
|
+
self.sio_server: Optional[AsyncServer] = None
|
|
176
242
|
self.metadata_key = metadata_key
|
|
243
|
+
self.enable_silence_timeout = enable_silence_timeout
|
|
177
244
|
|
|
178
245
|
self.jwt_key = jwt_key
|
|
179
246
|
self.jwt_algorithm = jwt_method
|
|
247
|
+
self.on_new_message: Optional[OnNewMessageType] = None
|
|
248
|
+
self.sender_silence_map: Dict[str, Task] = {}
|
|
180
249
|
|
|
181
|
-
def get_output_channel(self) -> Optional[
|
|
250
|
+
def get_output_channel(self) -> Optional[OutputChannel]:
|
|
182
251
|
"""Creates socket.io output channel object."""
|
|
183
|
-
if self.
|
|
252
|
+
if self.sio_server is None:
|
|
184
253
|
rasa.shared.utils.io.raise_warning(
|
|
185
254
|
"SocketIO output channel cannot be recreated. "
|
|
186
255
|
"This is expected behavior when using multiple Sanic "
|
|
@@ -189,7 +258,7 @@ class SocketIOInput(InputChannel):
|
|
|
189
258
|
"scenarios."
|
|
190
259
|
)
|
|
191
260
|
return None
|
|
192
|
-
return SocketIOOutput(self.
|
|
261
|
+
return SocketIOOutput(self, self.sio_server, self.bot_message_evt)
|
|
193
262
|
|
|
194
263
|
async def handle_session_request(
|
|
195
264
|
self, sid: Text, data: Optional[Dict] = None
|
|
@@ -200,23 +269,26 @@ class SocketIOInput(InputChannel):
|
|
|
200
269
|
if "session_id" not in data or data["session_id"] is None:
|
|
201
270
|
data["session_id"] = uuid.uuid4().hex
|
|
202
271
|
if self.session_persistence:
|
|
203
|
-
if inspect.iscoroutinefunction(self.
|
|
204
|
-
await self.
|
|
272
|
+
if inspect.iscoroutinefunction(self.sio_server.enter_room): # type: ignore[union-attr]
|
|
273
|
+
await self.sio_server.enter_room(sid, data["session_id"]) # type: ignore[union-attr]
|
|
205
274
|
else:
|
|
206
275
|
# for backwards compatibility with python-socketio < 5.10.
|
|
207
276
|
# previously, this function was NOT async.
|
|
208
|
-
self.
|
|
209
|
-
await self.
|
|
210
|
-
logger.debug(
|
|
277
|
+
self.sio_server.enter_room(sid, data["session_id"]) # type: ignore[union-attr]
|
|
278
|
+
await self.sio_server.emit("session_confirm", data["session_id"], room=sid) # type: ignore[union-attr]
|
|
279
|
+
logger.debug(
|
|
280
|
+
"socketio_channel.input.handle_session_request",
|
|
281
|
+
message=f"User {sid} connected to socketIO endpoint.",
|
|
282
|
+
)
|
|
211
283
|
|
|
212
284
|
async def handle_user_message(
|
|
213
285
|
self,
|
|
214
286
|
sid: Text,
|
|
215
287
|
data: Dict,
|
|
216
|
-
on_new_message:
|
|
288
|
+
on_new_message: OnNewMessageType,
|
|
217
289
|
) -> None:
|
|
218
290
|
"""Handles user messages received from the client."""
|
|
219
|
-
output_channel =
|
|
291
|
+
output_channel = self.get_output_channel()
|
|
220
292
|
|
|
221
293
|
if self.session_persistence:
|
|
222
294
|
if not data.get("session_id"):
|
|
@@ -232,38 +304,44 @@ class SocketIOInput(InputChannel):
|
|
|
232
304
|
else:
|
|
233
305
|
sender_id = sid
|
|
234
306
|
|
|
307
|
+
# We cancel silence timeout when a new message is received
|
|
308
|
+
# to prevent sending a silence timeout message
|
|
309
|
+
# if the user sends a message after the silence timeout
|
|
310
|
+
self._cancel_silence_timeout(sender_id)
|
|
311
|
+
|
|
235
312
|
metadata = data.get(self.metadata_key, {})
|
|
236
313
|
if isinstance(metadata, Text):
|
|
237
314
|
metadata = json.loads(metadata)
|
|
238
315
|
|
|
239
316
|
message = UserMessage(
|
|
240
|
-
data.get("message", ""),
|
|
241
|
-
output_channel,
|
|
242
|
-
sender_id,
|
|
317
|
+
text=data.get("message", ""),
|
|
318
|
+
output_channel=output_channel,
|
|
319
|
+
sender_id=sender_id,
|
|
243
320
|
input_channel=self.name(),
|
|
244
321
|
metadata=metadata,
|
|
245
322
|
)
|
|
246
323
|
await on_new_message(message)
|
|
247
324
|
|
|
248
|
-
def blueprint(
|
|
249
|
-
self, on_new_message: Callable[[UserMessage], Awaitable[Any]]
|
|
250
|
-
) -> SocketBlueprint:
|
|
325
|
+
def blueprint(self, on_new_message: OnNewMessageType) -> SocketBlueprint:
|
|
251
326
|
"""Defines a Sanic blueprint."""
|
|
252
327
|
# Workaround so that socketio works with requests from other origins.
|
|
253
328
|
# https://github.com/miguelgrinberg/python-socketio/issues/205#issuecomment-493769183
|
|
254
|
-
|
|
329
|
+
sio_server = AsyncServer(async_mode="sanic", cors_allowed_origins=[])
|
|
255
330
|
socketio_webhook = SocketBlueprint(
|
|
256
|
-
|
|
331
|
+
sio_server, self.socketio_path, "socketio_webhook", __name__
|
|
257
332
|
)
|
|
258
333
|
|
|
259
|
-
# make
|
|
260
|
-
self.
|
|
334
|
+
# make sio_server object static to use in get_output_channel
|
|
335
|
+
self.sio_server = sio_server
|
|
336
|
+
# We need to store the on_new_message callback
|
|
337
|
+
# so that we can call it when a silence timeout occurs
|
|
338
|
+
self.on_new_message = on_new_message
|
|
261
339
|
|
|
262
340
|
@socketio_webhook.route("/", methods=["GET"])
|
|
263
341
|
async def health(_: Request) -> HTTPResponse:
|
|
264
342
|
return response.json({"status": "ok"})
|
|
265
343
|
|
|
266
|
-
@
|
|
344
|
+
@sio_server.on("connect", namespace=self.namespace)
|
|
267
345
|
async def connect(sid: Text, environ: Dict, auth: Optional[Dict]) -> bool:
|
|
268
346
|
if self.jwt_key:
|
|
269
347
|
jwt_payload = None
|
|
@@ -273,24 +351,107 @@ class SocketIOInput(InputChannel):
|
|
|
273
351
|
)
|
|
274
352
|
|
|
275
353
|
if jwt_payload:
|
|
276
|
-
logger.debug(
|
|
354
|
+
logger.debug(
|
|
355
|
+
"socketio_channel.input.connect.jwt",
|
|
356
|
+
message=f"User {sid} connected to socketIO endpoint.",
|
|
357
|
+
)
|
|
358
|
+
# Store the chat state for this user
|
|
277
359
|
return True
|
|
278
360
|
else:
|
|
279
361
|
return False
|
|
280
362
|
else:
|
|
281
|
-
logger.debug(
|
|
363
|
+
logger.debug(
|
|
364
|
+
"socketio_channel.input.connect",
|
|
365
|
+
message=f"User {sid} connected to socketIO endpoint.",
|
|
366
|
+
)
|
|
367
|
+
# Store the chat state for this user
|
|
282
368
|
return True
|
|
283
369
|
|
|
284
|
-
@
|
|
370
|
+
@sio_server.on("disconnect", namespace=self.namespace)
|
|
285
371
|
async def disconnect(sid: Text) -> None:
|
|
286
|
-
logger.debug(
|
|
372
|
+
logger.debug(
|
|
373
|
+
"socketio_channel.input.disconnect",
|
|
374
|
+
message=f"User {sid} disconnected from socketIO endpoint.",
|
|
375
|
+
)
|
|
287
376
|
|
|
288
|
-
@
|
|
377
|
+
@sio_server.on("session_request", namespace=self.namespace)
|
|
289
378
|
async def session_request(sid: Text, data: Optional[Dict]) -> None:
|
|
379
|
+
logger.debug(
|
|
380
|
+
"socketio_channel.input.session_request",
|
|
381
|
+
message=f"User {sid} requested a session.",
|
|
382
|
+
)
|
|
290
383
|
await self.handle_session_request(sid, data)
|
|
291
384
|
|
|
292
|
-
@
|
|
385
|
+
@sio_server.on(self.user_message_evt, namespace=self.namespace)
|
|
293
386
|
async def handle_message(sid: Text, data: Dict) -> None:
|
|
387
|
+
logger.debug(
|
|
388
|
+
"socketio_channel.input.handle_message",
|
|
389
|
+
message=f"User {sid} sent a message.",
|
|
390
|
+
data=data,
|
|
391
|
+
)
|
|
294
392
|
await self.handle_user_message(sid, data, on_new_message)
|
|
295
393
|
|
|
296
394
|
return socketio_webhook
|
|
395
|
+
|
|
396
|
+
def reset_silence_timeout(self, silence_timeout: SilenceTimeout) -> None:
|
|
397
|
+
self._cancel_silence_timeout(silence_timeout.sender_id)
|
|
398
|
+
|
|
399
|
+
self.sender_silence_map[silence_timeout.sender_id] = (
|
|
400
|
+
asyncio.get_event_loop().create_task(
|
|
401
|
+
self._monitor_silence_timeout(
|
|
402
|
+
silence_timeout,
|
|
403
|
+
)
|
|
404
|
+
)
|
|
405
|
+
)
|
|
406
|
+
|
|
407
|
+
def disconnect(self, sender_id: str) -> None:
|
|
408
|
+
"""Disconnects the user with the given sender ID."""
|
|
409
|
+
self._cancel_silence_timeout(sender_id)
|
|
410
|
+
if self.sio_server:
|
|
411
|
+
asyncio.get_event_loop().create_task(self.sio_server.disconnect(sender_id))
|
|
412
|
+
logger.debug(
|
|
413
|
+
"socketio_channel.input.disconnect",
|
|
414
|
+
message=f"User {sender_id} disconnected from socketIO endpoint.",
|
|
415
|
+
)
|
|
416
|
+
|
|
417
|
+
async def _monitor_silence_timeout(self, silence_timeout: SilenceTimeout) -> None:
|
|
418
|
+
logger.debug(
|
|
419
|
+
"socketio_channel.input.silence_timeout_watch_started",
|
|
420
|
+
sender_id=silence_timeout.sender_id,
|
|
421
|
+
timeout=silence_timeout.timeout,
|
|
422
|
+
)
|
|
423
|
+
await asyncio.sleep(silence_timeout.timeout)
|
|
424
|
+
|
|
425
|
+
# once the timer is up, we call the handler
|
|
426
|
+
# to notify the user about the silence timeout
|
|
427
|
+
# this is important if monitoring trask is cancelled while handler is executed
|
|
428
|
+
asyncio.get_event_loop().create_task(
|
|
429
|
+
self._handle_silence_timeout(silence_timeout)
|
|
430
|
+
)
|
|
431
|
+
|
|
432
|
+
logger.debug(
|
|
433
|
+
"socketio_channel.input.silence_timeout_tripped",
|
|
434
|
+
sender_id=silence_timeout.sender_id,
|
|
435
|
+
silence_timeout=silence_timeout.timeout,
|
|
436
|
+
)
|
|
437
|
+
|
|
438
|
+
async def _handle_silence_timeout(self, event: SilenceTimeout) -> None:
|
|
439
|
+
if self.on_new_message:
|
|
440
|
+
output_channel = self.get_output_channel()
|
|
441
|
+
message = UserMessage(
|
|
442
|
+
text=USER_CONVERSATION_SILENCE_TIMEOUT,
|
|
443
|
+
output_channel=output_channel,
|
|
444
|
+
sender_id=event.sender_id,
|
|
445
|
+
input_channel=self.name(),
|
|
446
|
+
)
|
|
447
|
+
await self.on_new_message(message)
|
|
448
|
+
|
|
449
|
+
def _cancel_silence_timeout(self, sender_id: str) -> None:
|
|
450
|
+
"""Cancels the silence timeout task for the given sender."""
|
|
451
|
+
task = self.sender_silence_map.pop(sender_id, None)
|
|
452
|
+
if task and not task.done():
|
|
453
|
+
logger.debug(
|
|
454
|
+
"socketio_channel.input.silence_timeout_cancelled",
|
|
455
|
+
sender_id=sender_id,
|
|
456
|
+
)
|
|
457
|
+
task.cancel()
|
|
@@ -47,7 +47,6 @@ if TYPE_CHECKING:
|
|
|
47
47
|
from sanic import Sanic, Websocket # type: ignore[attr-defined]
|
|
48
48
|
from socketio import AsyncServer
|
|
49
49
|
|
|
50
|
-
from rasa.core.channels.channel import UserMessage
|
|
51
50
|
from rasa.shared.core.trackers import DialogueStateTracker
|
|
52
51
|
|
|
53
52
|
|
|
@@ -153,6 +152,7 @@ class StudioChatInput(SocketIOInput, VoiceInputChannel):
|
|
|
153
152
|
jwt_key: Optional[Text] = None,
|
|
154
153
|
jwt_method: Optional[Text] = "HS256",
|
|
155
154
|
metadata_key: Optional[Text] = "metadata",
|
|
155
|
+
enable_silence_timeout: bool = False,
|
|
156
156
|
) -> None:
|
|
157
157
|
"""Creates a `StudioChatInput` object."""
|
|
158
158
|
from rasa.core.agent import Agent
|
|
@@ -170,6 +170,7 @@ class StudioChatInput(SocketIOInput, VoiceInputChannel):
|
|
|
170
170
|
jwt_key=jwt_key,
|
|
171
171
|
jwt_method=jwt_method,
|
|
172
172
|
metadata_key=metadata_key,
|
|
173
|
+
enable_silence_timeout=enable_silence_timeout,
|
|
173
174
|
)
|
|
174
175
|
|
|
175
176
|
# Initialize the Voice Input Channel
|
|
@@ -210,14 +211,15 @@ class StudioChatInput(SocketIOInput, VoiceInputChannel):
|
|
|
210
211
|
jwt_key=credentials.get("jwt_key"),
|
|
211
212
|
jwt_method=credentials.get("jwt_method", "HS256"),
|
|
212
213
|
metadata_key=credentials.get("metadata_key", "metadata"),
|
|
214
|
+
enable_silence_timeout=credentials.get("enable_silence_timeout", False),
|
|
213
215
|
)
|
|
214
216
|
|
|
215
217
|
async def emit(self, event: str, data: str, room: str) -> None:
|
|
216
218
|
"""Emits an event to the websocket."""
|
|
217
|
-
if not self.
|
|
219
|
+
if not self.sio_server:
|
|
218
220
|
structlogger.error("studio_chat.emit.sio_not_initialized")
|
|
219
221
|
return
|
|
220
|
-
await self.
|
|
222
|
+
await self.sio_server.emit(event, data, room=room)
|
|
221
223
|
|
|
222
224
|
def _register_tracker_update_hook(self) -> None:
|
|
223
225
|
plugin_manager().register(StudioTrackerUpdatePlugin(self))
|
|
@@ -250,8 +252,8 @@ class StudioChatInput(SocketIOInput, VoiceInputChannel):
|
|
|
250
252
|
|
|
251
253
|
async def on_message_proxy(
|
|
252
254
|
self,
|
|
253
|
-
on_new_message: Callable[[
|
|
254
|
-
message:
|
|
255
|
+
on_new_message: Callable[[UserMessage], Awaitable[Any]],
|
|
256
|
+
message: UserMessage,
|
|
255
257
|
) -> None:
|
|
256
258
|
"""Proxies the on_new_message call to the underlying channel.
|
|
257
259
|
|
|
@@ -377,7 +379,7 @@ class StudioChatInput(SocketIOInput, VoiceInputChannel):
|
|
|
377
379
|
call_state.is_bot_speaking = True
|
|
378
380
|
return ContinueConversationAction()
|
|
379
381
|
|
|
380
|
-
def
|
|
382
|
+
def _create_output_channel(
|
|
381
383
|
self, voice_websocket: "Websocket", tts_engine: TTSEngine
|
|
382
384
|
) -> VoiceOutputChannel:
|
|
383
385
|
"""Create a voice output channel."""
|
|
@@ -407,7 +409,7 @@ class StudioChatInput(SocketIOInput, VoiceInputChannel):
|
|
|
407
409
|
|
|
408
410
|
# Create a websocket adapter for this connection
|
|
409
411
|
ws_adapter = SocketIOVoiceWebsocketAdapter(
|
|
410
|
-
|
|
412
|
+
sio_server=self.sio_server,
|
|
411
413
|
session_id=session_id,
|
|
412
414
|
sid=sid,
|
|
413
415
|
bot_message_evt=self.bot_message_evt,
|
|
@@ -455,12 +457,12 @@ class StudioChatInput(SocketIOInput, VoiceInputChannel):
|
|
|
455
457
|
task.cancel()
|
|
456
458
|
|
|
457
459
|
def blueprint(
|
|
458
|
-
self, on_new_message: Callable[[
|
|
460
|
+
self, on_new_message: Callable[[UserMessage], Awaitable[Any]]
|
|
459
461
|
) -> SocketBlueprint:
|
|
460
462
|
proxied_on_message = partial(self.on_message_proxy, on_new_message)
|
|
461
463
|
socket_blueprint = super().blueprint(proxied_on_message)
|
|
462
464
|
|
|
463
|
-
if not self.
|
|
465
|
+
if not self.sio_server:
|
|
464
466
|
structlogger.error("studio_chat.blueprint.sio_not_initialized")
|
|
465
467
|
return socket_blueprint
|
|
466
468
|
|
|
@@ -470,12 +472,12 @@ class StudioChatInput(SocketIOInput, VoiceInputChannel):
|
|
|
470
472
|
) -> None:
|
|
471
473
|
self.agent = app.ctx.agent
|
|
472
474
|
|
|
473
|
-
@self.
|
|
475
|
+
@self.sio_server.on("disconnect", namespace=self.namespace)
|
|
474
476
|
async def disconnect(sid: Text) -> None:
|
|
475
477
|
structlogger.debug("studio_chat.sio.disconnect", sid=sid)
|
|
476
478
|
self._cleanup_tasks_for_sid(sid)
|
|
477
479
|
|
|
478
|
-
@self.
|
|
480
|
+
@self.sio_server.on("session_request", namespace=self.namespace)
|
|
479
481
|
async def session_request(sid: Text, data: Optional[Dict]) -> None:
|
|
480
482
|
"""Overrides the base SocketIOInput session_request handler.
|
|
481
483
|
|
|
@@ -495,7 +497,7 @@ class StudioChatInput(SocketIOInput, VoiceInputChannel):
|
|
|
495
497
|
if data and data.get("is_voice", False):
|
|
496
498
|
self._start_voice_session(data["session_id"], sid, proxied_on_message)
|
|
497
499
|
|
|
498
|
-
@self.
|
|
500
|
+
@self.sio_server.on(self.user_message_evt, namespace=self.namespace)
|
|
499
501
|
async def handle_message(sid: Text, data: Dict) -> None:
|
|
500
502
|
"""Overrides the base SocketIOInput handle_message handler."""
|
|
501
503
|
# Handle voice messages
|
|
@@ -509,7 +511,7 @@ class StudioChatInput(SocketIOInput, VoiceInputChannel):
|
|
|
509
511
|
# Handle text messages
|
|
510
512
|
await self.handle_user_message(sid, data, proxied_on_message)
|
|
511
513
|
|
|
512
|
-
@self.
|
|
514
|
+
@self.sio_server.on("update_tracker", namespace=self.namespace)
|
|
513
515
|
async def on_update_tracker(sid: Text, data: Dict) -> None:
|
|
514
516
|
await self.handle_tracker_update(sid, data)
|
|
515
517
|
|
|
@@ -555,9 +557,9 @@ class SocketIOVoiceWebsocketAdapter:
|
|
|
555
557
|
"""Adapter to make Socket.IO work like a Sanic WebSocket for voice channels."""
|
|
556
558
|
|
|
557
559
|
def __init__(
|
|
558
|
-
self,
|
|
560
|
+
self, sio_server: "AsyncServer", session_id: str, sid: str, bot_message_evt: str
|
|
559
561
|
) -> None:
|
|
560
|
-
self.
|
|
562
|
+
self.sio_server = sio_server
|
|
561
563
|
self.bot_message_evt = bot_message_evt
|
|
562
564
|
self._closed = False
|
|
563
565
|
self._receive_queue: asyncio.Queue[Any] = asyncio.Queue()
|
|
@@ -576,7 +578,7 @@ class SocketIOVoiceWebsocketAdapter:
|
|
|
576
578
|
async def send(self, data: Any) -> None:
|
|
577
579
|
"""Send data to the client."""
|
|
578
580
|
if not self.closed:
|
|
579
|
-
await self.
|
|
581
|
+
await self.sio_server.emit(self.bot_message_evt, data, room=self.sid)
|
|
580
582
|
|
|
581
583
|
async def recv(self) -> Any:
|
|
582
584
|
"""Receive data from the client."""
|
|
@@ -11,6 +11,11 @@ from sanic import Websocket # type: ignore
|
|
|
11
11
|
from sanic.exceptions import ServerError, WebsocketClosed
|
|
12
12
|
|
|
13
13
|
from rasa.core.channels import InputChannel, OutputChannel, UserMessage
|
|
14
|
+
from rasa.core.channels.constants import (
|
|
15
|
+
USER_CONVERSATION_SESSION_END,
|
|
16
|
+
USER_CONVERSATION_SESSION_START,
|
|
17
|
+
USER_CONVERSATION_SILENCE_TIMEOUT,
|
|
18
|
+
)
|
|
14
19
|
from rasa.core.channels.voice_ready.utils import (
|
|
15
20
|
CallParameters,
|
|
16
21
|
validate_voice_license_scope,
|
|
@@ -48,9 +53,6 @@ from rasa.utils.io import remove_emojis
|
|
|
48
53
|
logger = structlog.get_logger(__name__)
|
|
49
54
|
|
|
50
55
|
# define constants for the voice channel
|
|
51
|
-
USER_CONVERSATION_SESSION_END = "/session_end"
|
|
52
|
-
USER_CONVERSATION_SESSION_START = "/session_start"
|
|
53
|
-
USER_CONVERSATION_SILENCE_TIMEOUT = "/silence_timeout"
|
|
54
56
|
|
|
55
57
|
|
|
56
58
|
@dataclass
|
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
|
|
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 =
|
|
266
|
+
input_channels = create_input_channels(channel, credentials)
|
|
257
267
|
|
|
258
268
|
if inspect:
|
|
259
269
|
logger.info("Starting development inspector.")
|
rasa/model_manager/model_api.py
CHANGED
|
@@ -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
|
-
|
|
575
|
-
bp = SocketBlueprint(
|
|
574
|
+
sio_server = AsyncServer(async_mode="sanic", cors_allowed_origins="*")
|
|
575
|
+
bp = SocketBlueprint(sio_server, "", "model_api_external")
|
|
576
576
|
|
|
577
|
-
create_bridge_server(
|
|
577
|
+
create_bridge_server(sio_server, running_bots)
|
|
578
578
|
|
|
579
579
|
@bp.get("/health")
|
|
580
580
|
async def health(request: Request) -> response.HTTPResponse:
|
|
@@ -19,7 +19,7 @@ socket_proxy_clients = {}
|
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
async def socketio_websocket_traffic_wrapper(
|
|
22
|
-
|
|
22
|
+
sio_server: AsyncServer,
|
|
23
23
|
running_bots: Dict[str, BotSession],
|
|
24
24
|
sid: str,
|
|
25
25
|
auth: Optional[Dict],
|
|
@@ -55,7 +55,9 @@ async def socketio_websocket_traffic_wrapper(
|
|
|
55
55
|
structlogger.error("model_runner.bot_not_alive", deployment_id=deployment_id)
|
|
56
56
|
raise ConnectionRefusedError("model_runner.bot_not_alive")
|
|
57
57
|
|
|
58
|
-
client = await create_bridge_client(
|
|
58
|
+
client = await create_bridge_client(
|
|
59
|
+
sio_server, bot.internal_url, sid, deployment_id
|
|
60
|
+
)
|
|
59
61
|
|
|
60
62
|
if client.sid is not None:
|
|
61
63
|
structlogger.debug(
|
|
@@ -70,20 +72,24 @@ async def socketio_websocket_traffic_wrapper(
|
|
|
70
72
|
raise ConnectionRefusedError("model_runner.bot_connection_failed")
|
|
71
73
|
|
|
72
74
|
|
|
73
|
-
def create_bridge_server(
|
|
75
|
+
def create_bridge_server(
|
|
76
|
+
sio_server: AsyncServer, running_bots: Dict[str, BotSession]
|
|
77
|
+
) -> None:
|
|
74
78
|
"""Create handlers for the socket server side.
|
|
75
79
|
|
|
76
80
|
Forwards messages coming from the user to the bot.
|
|
77
81
|
"""
|
|
78
82
|
|
|
79
|
-
@
|
|
83
|
+
@sio_server.on("connect")
|
|
80
84
|
async def socketio_websocket_traffic(
|
|
81
85
|
sid: str, environ: Dict, auth: Optional[Dict]
|
|
82
86
|
) -> bool:
|
|
83
87
|
"""Bridge websockets between user chat socket and bot server."""
|
|
84
|
-
return await socketio_websocket_traffic_wrapper(
|
|
88
|
+
return await socketio_websocket_traffic_wrapper(
|
|
89
|
+
sio_server, running_bots, sid, auth
|
|
90
|
+
)
|
|
85
91
|
|
|
86
|
-
@
|
|
92
|
+
@sio_server.on("disconnect")
|
|
87
93
|
async def disconnect(sid: str) -> None:
|
|
88
94
|
"""Disconnect the bot connection."""
|
|
89
95
|
structlogger.debug("model_runner.bot_disconnect", sid=sid)
|
|
@@ -91,7 +97,7 @@ def create_bridge_server(sio: AsyncServer, running_bots: Dict[str, BotSession])
|
|
|
91
97
|
await socket_proxy_clients[sid].disconnect()
|
|
92
98
|
del socket_proxy_clients[sid]
|
|
93
99
|
|
|
94
|
-
@
|
|
100
|
+
@sio_server.on("*")
|
|
95
101
|
async def handle_message(event: str, sid: str, data: Dict[str, Any]) -> None:
|
|
96
102
|
"""Bridge messages between user and bot.
|
|
97
103
|
|
|
@@ -108,7 +114,7 @@ def create_bridge_server(sio: AsyncServer, running_bots: Dict[str, BotSession])
|
|
|
108
114
|
|
|
109
115
|
|
|
110
116
|
async def create_bridge_client(
|
|
111
|
-
|
|
117
|
+
sio_server: AsyncServer, url: str, sid: str, deployment_id: str
|
|
112
118
|
) -> AsyncClient:
|
|
113
119
|
"""Create a new socket bridge client.
|
|
114
120
|
|
|
@@ -123,36 +129,36 @@ async def create_bridge_client(
|
|
|
123
129
|
structlogger.debug(
|
|
124
130
|
"model_runner.bot_session_confirmed", deployment_id=deployment_id
|
|
125
131
|
)
|
|
126
|
-
await
|
|
132
|
+
await sio_server.emit("session_confirm", room=sid)
|
|
127
133
|
|
|
128
134
|
@client.event # type: ignore[misc]
|
|
129
135
|
async def bot_message(data: Dict[str, Any]) -> None:
|
|
130
136
|
structlogger.debug("model_runner.bot_message", deployment_id=deployment_id)
|
|
131
|
-
await
|
|
137
|
+
await sio_server.emit("bot_message", data, room=sid)
|
|
132
138
|
|
|
133
139
|
@client.event # type: ignore[misc]
|
|
134
140
|
async def error(data: Dict[str, Any]) -> None:
|
|
135
141
|
structlogger.debug(
|
|
136
142
|
"model_runner.bot_error", deployment_id=deployment_id, data=data
|
|
137
143
|
)
|
|
138
|
-
await
|
|
144
|
+
await sio_server.emit("error", data, room=sid)
|
|
139
145
|
|
|
140
146
|
@client.event # type: ignore[misc]
|
|
141
147
|
async def tracker(data: Dict[str, Any]) -> None:
|
|
142
|
-
await
|
|
148
|
+
await sio_server.emit("tracker", json.loads(data), room=sid)
|
|
143
149
|
|
|
144
150
|
@client.event # type: ignore[misc]
|
|
145
151
|
async def disconnect() -> None:
|
|
146
152
|
structlogger.debug(
|
|
147
153
|
"model_runner.bot_connection_closed", deployment_id=deployment_id
|
|
148
154
|
)
|
|
149
|
-
await
|
|
155
|
+
await sio_server.emit("disconnect", room=sid)
|
|
150
156
|
|
|
151
157
|
@client.event # type: ignore[misc]
|
|
152
158
|
async def connect_error() -> None:
|
|
153
159
|
structlogger.error(
|
|
154
160
|
"model_runner.bot_connection_error", deployment_id=deployment_id
|
|
155
161
|
)
|
|
156
|
-
await
|
|
162
|
+
await sio_server.emit("disconnect", room=sid)
|
|
157
163
|
|
|
158
164
|
return client
|
rasa/shared/providers/_utils.py
CHANGED
|
@@ -1,87 +1,103 @@
|
|
|
1
1
|
from typing import Any, Dict, Optional
|
|
2
2
|
|
|
3
|
+
import boto3
|
|
3
4
|
import structlog
|
|
4
|
-
from
|
|
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
|
-
|
|
11
|
+
AWS_BEDROCK_PROVIDER,
|
|
11
12
|
AWS_REGION_NAME_CONFIG_KEY,
|
|
12
|
-
|
|
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.
|
|
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:
|
|
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
|
|
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
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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"
|
|
77
|
-
f"
|
|
78
|
-
f"
|
|
79
|
-
f"
|
|
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}.
|
|
97
|
+
f"{source_log}.validate_aws_credentials_for_litellm_clients",
|
|
83
98
|
event_info=event_info,
|
|
84
|
-
|
|
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/version.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: rasa-pro
|
|
3
|
-
Version: 3.14.0.
|
|
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
|
|
@@ -108,9 +108,10 @@ rasa/core/brokers/sql.py,sha256=SZ-ZFVaPnkCPNNKnISf3KQ-HnG4mQhZ4AoLJrkrIfUE,2748
|
|
|
108
108
|
rasa/core/channels/__init__.py,sha256=cK3Yk6BChYPd01LIt-XPftaN5obucxTQTEpNuzkdKSs,2624
|
|
109
109
|
rasa/core/channels/botframework.py,sha256=yMtg-xIsH8t_26dCOpR7V4eCrRPYumM5KhGvdNhHYl4,11668
|
|
110
110
|
rasa/core/channels/callback.py,sha256=Llt5TGimf_5P29s2KxEPOZUX6_df7u8uBCsNSFy7CQA,2750
|
|
111
|
-
rasa/core/channels/channel.py,sha256=
|
|
111
|
+
rasa/core/channels/channel.py,sha256=q8zq9n86kb9iccXoGv_Vj4vcwB75AlXnoK5vg_3evhE,18730
|
|
112
112
|
rasa/core/channels/console.py,sha256=13bjhsmnuKoShYVdtt2VHzzt9xylER-hDTONC1MiQG0,8075
|
|
113
|
-
rasa/core/channels/
|
|
113
|
+
rasa/core/channels/constants.py,sha256=HWLmyE1kuq9veQFVfAJuGLm3I3Y_IvzEHAJ7PoSxbLQ,153
|
|
114
|
+
rasa/core/channels/development_inspector.py,sha256=J4Ip9B7TuUjbqutoS7fBRH0cFfIpqoBjlpdFn7xvKAM,10794
|
|
114
115
|
rasa/core/channels/facebook.py,sha256=7DTNz2hKLG7sm0U6UjqPgedZthZXrWFnLEW40c8OrCg,15742
|
|
115
116
|
rasa/core/channels/hangouts.py,sha256=MlHX-KKhF0D-f4EtPAN56e7N1ZuvupTxLl4MXMsCbQg,11732
|
|
116
117
|
rasa/core/channels/inspector/.eslintrc.cjs,sha256=FAnPE1mm-eiGeQb9JLMVxDtxX4YOQG4wEFM6OFHB74Y,721
|
|
@@ -253,8 +254,8 @@ rasa/core/channels/rasa_chat.py,sha256=pRXn4NLHUCW0_D1FH2B87p7Lnf-xMfPxkjrpbnFXn
|
|
|
253
254
|
rasa/core/channels/rest.py,sha256=LWBYBdVzOz5Vv5tZCkB1QA7LxXJFTeC87CQLAi_ZGeI,7310
|
|
254
255
|
rasa/core/channels/rocketchat.py,sha256=hajaH6549CjEYFM5jSapw1DQKBPKTXbn7cVSuZzknmI,5999
|
|
255
256
|
rasa/core/channels/slack.py,sha256=jVsTTUu9wUjukPoIsAhbee9o0QFUMCNlQHbR8LTcMBc,24406
|
|
256
|
-
rasa/core/channels/socketio.py,sha256=
|
|
257
|
-
rasa/core/channels/studio_chat.py,sha256=
|
|
257
|
+
rasa/core/channels/socketio.py,sha256=N3aldDMzXotO85lX5pkmVtVq7g6gh9YpUpQpbAH3ogs,17560
|
|
258
|
+
rasa/core/channels/studio_chat.py,sha256=g_whnLDmPdGxR63dX2AmJWnPhRnrMxaJsnGz--cDen0,22907
|
|
258
259
|
rasa/core/channels/telegram.py,sha256=TKVknsk3U9tYeY1a8bzlhqkltWmZfGSOvrcmwa9qozc,12499
|
|
259
260
|
rasa/core/channels/twilio.py,sha256=2BTQpyx0b0yPpc0A2BHYfxLPgodrLGLs8nq6i3lVGAM,5906
|
|
260
261
|
rasa/core/channels/vier_cvg.py,sha256=5O4yx0TDQIMppvlCxTOzmPB60CA-vqQXqWQ7upfrTO0,13496
|
|
@@ -283,7 +284,7 @@ rasa/core/channels/voice_stream/tts/tts_cache.py,sha256=K4S2d8zWX2h2ylYALp7IdqFS
|
|
|
283
284
|
rasa/core/channels/voice_stream/tts/tts_engine.py,sha256=JMCWGHxT8QiqKoBeI6F4RX_-Q9EEqG3vUtkgOUnlt-w,1812
|
|
284
285
|
rasa/core/channels/voice_stream/twilio_media_streams.py,sha256=UbaSTaB0tUTf7ryToK6epxb5p31SpFk3tMNnFFo4q_E,9074
|
|
285
286
|
rasa/core/channels/voice_stream/util.py,sha256=nbr0yUl0NIn4-94VUYjPEg_NB2kadcFCq-i9rRJMv4U,2323
|
|
286
|
-
rasa/core/channels/voice_stream/voice_channel.py,sha256=
|
|
287
|
+
rasa/core/channels/voice_stream/voice_channel.py,sha256=loflCjR9nCZEtlpnR49jofOWsx7buhRyOsgc27rss-Q,23999
|
|
287
288
|
rasa/core/channels/webexteams.py,sha256=z_o_jnc6B7hsHpd6XorImFkF43wB4yx_kiTPKAjPSuo,4805
|
|
288
289
|
rasa/core/concurrent_lock_store.py,sha256=aAZDAYUVffCx2J8wbJ05vTE3Xd9bQ4Dx13RZmCy3ohw,8285
|
|
289
290
|
rasa/core/constants.py,sha256=dEokmEf6XkOFA_xpuwjqwNtlZv-a5Tz5dLMRc7Vu4CU,4070
|
|
@@ -339,7 +340,7 @@ rasa/core/policies/rule_policy.py,sha256=EItfUn07JIBLRIbriPKDprsvWq_-xzZTGrlTS2e
|
|
|
339
340
|
rasa/core/policies/ted_policy.py,sha256=0RzIuyrtt4PxLcqQ-bfaExkZvU-TnsMbgmDcwh2SakY,87710
|
|
340
341
|
rasa/core/policies/unexpected_intent_policy.py,sha256=ZXvbswf2NDy00kHmBQcyXa1OVYFyc79HQKrFkQ4gCfM,39609
|
|
341
342
|
rasa/core/processor.py,sha256=9bFLV7Thgde1XXsvkKbXqwsSz8QxaC2E88qAYmpEBuI,62314
|
|
342
|
-
rasa/core/run.py,sha256=
|
|
343
|
+
rasa/core/run.py,sha256=zwRHM0iXguU1HR___SnOUIwTsCVvSj3FEeiijEiO4Hw,12546
|
|
343
344
|
rasa/core/secrets_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
344
345
|
rasa/core/secrets_manager/constants.py,sha256=dTDHenvG1JBVi34QIR6FpdO5RDOXQwAjAxLlgJ2ZNEI,1193
|
|
345
346
|
rasa/core/secrets_manager/endpoints.py,sha256=4b7KXB9amdF23eYGsx8215bOjE5-TQ73qD2hdI8Sm9c,12662
|
|
@@ -432,7 +433,7 @@ rasa/dialogue_understanding/patterns/collect_information.py,sha256=8YWvhFTt8CJML
|
|
|
432
433
|
rasa/dialogue_understanding/patterns/completed.py,sha256=7qkyUj2d__2R3mpwWVmQpfwCCbJruBrjRZbmbDr3Zbo,1278
|
|
433
434
|
rasa/dialogue_understanding/patterns/continue_interrupted.py,sha256=OSTbe5l0B0ECNIYWpYB0pdzIeaqM3m3UZskNNjL5vrw,1682
|
|
434
435
|
rasa/dialogue_understanding/patterns/correction.py,sha256=7fQ02-JU1CGZiTjTi9YqmD1F4o-9Tv5WCAXnFgZlvtY,11380
|
|
435
|
-
rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml,sha256=
|
|
436
|
+
rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml,sha256=2rAErw1D6i_m7cxd2kbxf24xg5Y1duE3Kpx6O7j_Xdc,11038
|
|
436
437
|
rasa/dialogue_understanding/patterns/domain_for_patterns.py,sha256=Zv_lCJn4nbkxeNYOPGsR0V8tmYAUsM_Ho_9to8hku-o,6493
|
|
437
438
|
rasa/dialogue_understanding/patterns/human_handoff.py,sha256=1hkSdL6kui42rZc7zERZ9R7nLyvRHi_tHgNU7FyrhAQ,1132
|
|
438
439
|
rasa/dialogue_understanding/patterns/internal_error.py,sha256=APCKVv16M6mSQ4upu4UwG0yIaaKTyr7uB2yV8ZtpMzo,1609
|
|
@@ -563,9 +564,9 @@ rasa/markers/validate.py,sha256=dZvMTcDK_sji9OP8JY4kUcjeIScLF93C3CKTWK8DplI,708
|
|
|
563
564
|
rasa/model.py,sha256=cAbQXvfZXBKHAj79Z0-mCy29hSSWp2KaroScgDeTfJw,3489
|
|
564
565
|
rasa/model_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
565
566
|
rasa/model_manager/config.py,sha256=8upZP4CokMBy0imiiPvINJuLW4JOQ326dPiJ041jJUI,1231
|
|
566
|
-
rasa/model_manager/model_api.py,sha256=
|
|
567
|
+
rasa/model_manager/model_api.py,sha256=RZqQLFqlbcao0gnqpjN8EG6E29Ls2pxZiDnCPKoIhKw,23876
|
|
567
568
|
rasa/model_manager/runner_service.py,sha256=CfvkmCqk-syCqTxb4u3N44WvzXuWFlpomR9SvgzIFKs,9514
|
|
568
|
-
rasa/model_manager/socket_bridge.py,sha256=
|
|
569
|
+
rasa/model_manager/socket_bridge.py,sha256=S_7aijsXuqHSlSscJfNw2Kt_iWXGfhwn6WhKA3FBLr0,5689
|
|
569
570
|
rasa/model_manager/studio_jwt_auth.py,sha256=uls2QiHUlUrR3fOzZssW4UaAMJMfnPMZeV1aDmZIT0E,2645
|
|
570
571
|
rasa/model_manager/trainer_service.py,sha256=aw3tp2736fILT5bYunkERPcWR5TjjyhThBXIktJfhqU,10628
|
|
571
572
|
rasa/model_manager/utils.py,sha256=rS0ST-rJMuZOna90r_Ioz7gOkZ8r8vm4XAhzI0iUZOA,2643
|
|
@@ -739,13 +740,13 @@ rasa/shared/providers/_configs/rasa_llm_client_config.py,sha256=UiEVmjkoS3a0AHLQ
|
|
|
739
740
|
rasa/shared/providers/_configs/self_hosted_llm_client_config.py,sha256=rxUFj8s4HAukjhILlOs7vrgXL9WNlfvGEcfMVEa-LrA,5952
|
|
740
741
|
rasa/shared/providers/_configs/utils.py,sha256=AUnvh4qF9VfLoXpTPoZfwYQ9YsVW8HPMbWa-vG6wOHw,453
|
|
741
742
|
rasa/shared/providers/_ssl_verification_utils.py,sha256=vUnP0vocf0GQ0wG8IQpPcCet4c1C9-wQWQNckNWbDBk,4165
|
|
742
|
-
rasa/shared/providers/_utils.py,sha256=
|
|
743
|
+
rasa/shared/providers/_utils.py,sha256=LVPsZbl6zzF4hZE6bNVwgY4BkbeIWnRD0dhEqA-kWkk,6975
|
|
743
744
|
rasa/shared/providers/constants.py,sha256=hgV8yNGxIbID_2h65OoSfSjIE4UkazrsqRg4SdkPAmI,234
|
|
744
745
|
rasa/shared/providers/embedding/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
745
746
|
rasa/shared/providers/embedding/_base_litellm_embedding_client.py,sha256=1CUYxps_TrLVyPsPfOw7iDS502fDePseBIKnqc3ncwQ,9005
|
|
746
747
|
rasa/shared/providers/embedding/_langchain_embedding_client_adapter.py,sha256=IR2Rb3ReJ9C9sxOoOGRXgtz8STWdMREs_4AeSMKFjl4,2135
|
|
747
748
|
rasa/shared/providers/embedding/azure_openai_embedding_client.py,sha256=HKHMx6m669CC19u6GPnpSLzA0PwvHlquhaK3QhqHI78,12469
|
|
748
|
-
rasa/shared/providers/embedding/default_litellm_embedding_client.py,sha256=
|
|
749
|
+
rasa/shared/providers/embedding/default_litellm_embedding_client.py,sha256=FQUFglXN9Ty-FnUaA80itdyutDxLk7HRZwzULG02k8s,4520
|
|
749
750
|
rasa/shared/providers/embedding/embedding_client.py,sha256=LGFlnsf5B0XDn8GRn_mLfCJ5erhf2p3zXiKTdG9jNXY,2839
|
|
750
751
|
rasa/shared/providers/embedding/embedding_response.py,sha256=H55mSAL3LfVvDlBklaCCQ4AnNwCsQSQ1f2D0oPrx3FY,1204
|
|
751
752
|
rasa/shared/providers/embedding/huggingface_local_embedding_client.py,sha256=Zo3gyj49h4LxXV7bx39TXpIPKlernG-5xzqXczTCbig,6913
|
|
@@ -754,7 +755,7 @@ rasa/shared/providers/embedding/openai_embedding_client.py,sha256=XNRGE7apo2v3kW
|
|
|
754
755
|
rasa/shared/providers/llm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
755
756
|
rasa/shared/providers/llm/_base_litellm_client.py,sha256=Ua5Kt6VGe5vRzSzWWWx2Q3LH2PCDd8V7V4zfYD464yU,11634
|
|
756
757
|
rasa/shared/providers/llm/azure_openai_llm_client.py,sha256=ui85vothxR2P_-eLc4nLgbpjnpEKY2BXnIjLxBZoYz8,12504
|
|
757
|
-
rasa/shared/providers/llm/default_litellm_llm_client.py,sha256=
|
|
758
|
+
rasa/shared/providers/llm/default_litellm_llm_client.py,sha256=q6QoyPPq0K7V9aeD0zr08ZK69xlH4GseGFdhUxpWcG8,4210
|
|
758
759
|
rasa/shared/providers/llm/litellm_router_llm_client.py,sha256=_6vAdPLAVSI_sBJLaXLnE87M-0ip_klfQ78fQ_pyoyI,7947
|
|
759
760
|
rasa/shared/providers/llm/llm_client.py,sha256=-hTCRsL-A3GCMRHtcyCgcCyra-9OJ8GUC-mURoRXH0k,3242
|
|
760
761
|
rasa/shared/providers/llm/llm_response.py,sha256=8mOpZdmh4-3yM7aOmNO0yEYUmRDErfoP7ZDMUuHr2Cc,3504
|
|
@@ -847,9 +848,9 @@ rasa/utils/train_utils.py,sha256=ClJx-6x3-h3Vt6mskacgkcCUJTMXjFPe3zAcy_DfmaU,212
|
|
|
847
848
|
rasa/utils/url_tools.py,sha256=dZ1HGkVdWTJB7zYEdwoDIrEuyX9HE5WsxKKFVsXBLE0,1218
|
|
848
849
|
rasa/utils/yaml.py,sha256=KjbZq5C94ZP7Jdsw8bYYF7HASI6K4-C_kdHfrnPLpSI,2000
|
|
849
850
|
rasa/validator.py,sha256=fhRlHQvuBkiup0FnNYmwRmqQwC3QpdCJt0TuvW4jMaI,83125
|
|
850
|
-
rasa/version.py,sha256=
|
|
851
|
-
rasa_pro-3.14.0.
|
|
852
|
-
rasa_pro-3.14.0.
|
|
853
|
-
rasa_pro-3.14.0.
|
|
854
|
-
rasa_pro-3.14.0.
|
|
855
|
-
rasa_pro-3.14.0.
|
|
851
|
+
rasa/version.py,sha256=Ky7QZgOki_d2qRC7Z1GAZctbe6IxA52vhVkvPpQ_8pc,129
|
|
852
|
+
rasa_pro-3.14.0.dev20250825.dist-info/METADATA,sha256=V3KjozHtsQb15IjlJK2CYfppD_oVL17uPhmIx7pXJ18,10585
|
|
853
|
+
rasa_pro-3.14.0.dev20250825.dist-info/NOTICE,sha256=7HlBoMHJY9CL2GlYSfTQ-PZsVmLmVkYmMiPlTjhuCqA,218
|
|
854
|
+
rasa_pro-3.14.0.dev20250825.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
|
|
855
|
+
rasa_pro-3.14.0.dev20250825.dist-info/entry_points.txt,sha256=ckJ2SfEyTPgBqj_I6vm_tqY9dZF_LAPJZA335Xp0Q9U,43
|
|
856
|
+
rasa_pro-3.14.0.dev20250825.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
{rasa_pro-3.14.0.dev20250818.dist-info → rasa_pro-3.14.0.dev20250825.dist-info}/entry_points.txt
RENAMED
|
File without changes
|