rasa-pro 3.10.7.dev1__py3-none-any.whl → 3.10.7.dev3__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.

@@ -37,7 +37,11 @@ class SocketBlueprint(Blueprint):
37
37
  :param options: Options to be used while registering the
38
38
  blueprint into the app.
39
39
  """
40
- self.ctx.sio.attach(app, self.ctx.socketio_path)
40
+ if self.ctx.socketio_path:
41
+ path = self.ctx.socketio_path
42
+ else:
43
+ path = options.get("url_prefix", "/socket.io")
44
+ self.ctx.sio.attach(app, path)
41
45
  super().register(app, options)
42
46
 
43
47
 
@@ -1,6 +1,11 @@
1
1
  import sys
2
+ import os
2
3
 
3
- SERVER_BASE_WORKING_DIRECTORY = "working-data"
4
+ SERVER_BASE_WORKING_DIRECTORY = os.environ.get(
5
+ "RASA_MODEL_SERVER_BASE_DIRECTORY", "working-data"
6
+ )
7
+
8
+ SERVER_BASE_URL = os.environ.get("RASA_MODEL_SERVER_BASE_URL", None)
4
9
 
5
10
  # The path to the python executable that is running this script
6
11
  # we will use the same python to run training / bots
@@ -2,13 +2,14 @@ import asyncio
2
2
  import os
3
3
  from typing import Any, Dict, Optional
4
4
  import dotenv
5
- from sanic import Sanic, response
5
+ from sanic import Blueprint, Sanic, response
6
6
  from sanic.response import json
7
7
  from sanic.exceptions import NotFound
8
8
  from sanic.request import Request
9
9
  import structlog
10
10
  from socketio import AsyncServer
11
11
 
12
+ from rasa.model_manager.config import SERVER_BASE_URL
12
13
  from rasa.model_manager.runner_service import (
13
14
  BotSession,
14
15
  run_bot,
@@ -34,12 +35,6 @@ dotenv.load_dotenv()
34
35
 
35
36
  structlogger = structlog.get_logger()
36
37
 
37
- # configure the sanice application
38
- app = Sanic("RasaManager")
39
- # attach the socketio server to the sanic app
40
- sio = AsyncServer(async_mode="sanic", cors_allowed_origins=[])
41
- sio.attach(app, "/socket.io")
42
-
43
38
 
44
39
  # A simple in-memory store for training sessions and running bots
45
40
  trainings: Dict[str, TrainingSession] = {}
@@ -81,6 +76,14 @@ async def update_status_of_all_bots() -> None:
81
76
  await update_bot_status(bot)
82
77
 
83
78
 
79
+ def base_server_url(request: Request) -> str:
80
+ """Return the base URL of the server."""
81
+ if SERVER_BASE_URL:
82
+ return SERVER_BASE_URL
83
+ else:
84
+ return f"{request.scheme}://{request.host}"
85
+
86
+
84
87
  def get_log_url(request: Request, action_id: str) -> response.HTTPResponse:
85
88
  """Return a URL for downloading the log file for training / deployment."""
86
89
  if not os.path.exists(logs_path(action_id)):
@@ -88,7 +91,7 @@ def get_log_url(request: Request, action_id: str) -> response.HTTPResponse:
88
91
 
89
92
  return json(
90
93
  {
91
- "url": f"{request.scheme}://{request.host}/logs/{action_id}.txt",
94
+ "url": f"{base_server_url(request)}/logs/{action_id}.txt",
92
95
  "expires_in_seconds": 60 * 60 * 24,
93
96
  }
94
97
  )
@@ -113,14 +116,13 @@ def get_training_model_url(request: Request, training_id: str) -> response.HTTPR
113
116
 
114
117
  return json(
115
118
  {
116
- "url": f"{request.scheme}://{request.host}/models/{model}",
119
+ "url": f"{base_server_url(request)}/models/{model}",
117
120
  "expires_in_seconds": 60 * 60 * 24,
118
121
  }
119
122
  )
120
123
 
121
124
 
122
- @app.after_server_start
123
- async def continuously_update_process_status(running_app: Sanic) -> None:
125
+ async def continuously_update_process_status() -> None:
124
126
  """Regularly Update the status of all training and bot processes."""
125
127
  structlogger.debug("model_api.update_process_status.started")
126
128
 
@@ -130,295 +132,302 @@ async def continuously_update_process_status(running_app: Sanic) -> None:
130
132
  await asyncio.sleep(1)
131
133
 
132
134
 
133
- @app.before_server_stop
134
- async def cleanup_processes(app: Sanic, loop: asyncio.AbstractEventLoop) -> None:
135
- """Terminate all running processes before the server stops."""
136
- structlogger.debug("model_api.cleanup_processes.started")
137
- cleanup_training_processes()
138
- cleanup_bot_processes()
135
+ def internal_blueprint() -> Blueprint:
136
+ """Create a blueprint for the model manager API."""
137
+ bp = Blueprint("model_api_internal")
139
138
 
139
+ @bp.before_server_stop
140
+ async def cleanup_processes(app: Sanic, loop: asyncio.AbstractEventLoop) -> None:
141
+ """Terminate all running processes before the server stops."""
142
+ structlogger.debug("model_api.cleanup_processes.started")
143
+ cleanup_training_processes()
144
+ cleanup_bot_processes()
140
145
 
141
- @app.get("/")
142
- async def health(request: Request) -> response.HTTPResponse:
143
- return json(
144
- {
145
- "status": "ok",
146
- "bots": [
147
- {
148
- "deployment_id": bot.deployment_id,
149
- "status": bot.status,
150
- "internal_url": bot.internal_url,
151
- "url": bot.url,
152
- }
153
- for bot in running_bots.values()
154
- ],
155
- "trainings": [
146
+ @bp.get("/")
147
+ async def health(request: Request) -> response.HTTPResponse:
148
+ return json(
149
+ {
150
+ "status": "ok",
151
+ "bots": [
152
+ {
153
+ "deployment_id": bot.deployment_id,
154
+ "status": bot.status,
155
+ "internal_url": bot.internal_url,
156
+ "url": bot.url,
157
+ }
158
+ for bot in running_bots.values()
159
+ ],
160
+ "trainings": [
161
+ {
162
+ "training_id": training.training_id,
163
+ "assistant_id": training.assistant_id,
164
+ "client_id": training.client_id,
165
+ "progress": training.progress,
166
+ "status": training.status,
167
+ }
168
+ for training in trainings.values()
169
+ ],
170
+ }
171
+ )
172
+
173
+ @bp.get("/training")
174
+ async def get_training_list(request: Request) -> response.HTTPResponse:
175
+ """Return a list of all training sessions for an assistant."""
176
+ assistant_id = request.args.get("assistant_id")
177
+ sessions = [
178
+ {
179
+ "training_id": session.training_id,
180
+ "assistant_id": session.assistant_id,
181
+ "client_id": session.client_id,
182
+ "training_runtime": "self",
183
+ "status": session.status,
184
+ "bot_config": None,
185
+ "logs": None,
186
+ "metadata": None,
187
+ "model": None,
188
+ "runtime_metadata": None,
189
+ }
190
+ for session in trainings.values()
191
+ if session.assistant_id == assistant_id
192
+ ]
193
+ return json({"training_sessions": sessions, "total_number": len(sessions)})
194
+
195
+ @bp.post("/training")
196
+ async def start_training(request: Request) -> response.HTTPResponse:
197
+ """Start a new training session."""
198
+ data = request.json
199
+ training_id: Optional[str] = data.get("id")
200
+ assistant_id: Optional[str] = data.get("assistant_id")
201
+ client_id: Optional[str] = data.get("client_id")
202
+
203
+ if training_id in trainings:
204
+ # fail, because there apparently is already a training with this id
205
+ return json({"message": "Training with this id already exists"}, status=409)
206
+
207
+ if not assistant_id:
208
+ return json({"message": "Assistant id is required"}, status=400)
209
+
210
+ if not training_id:
211
+ return json({"message": "Training id is required"}, status=400)
212
+
213
+ try:
214
+ training_session = run_training(
215
+ training_id=training_id,
216
+ assistant_id=assistant_id,
217
+ client_id=client_id,
218
+ data=data,
219
+ )
220
+ trainings[training_id] = training_session
221
+ return json({"training_id": training_id})
222
+ except Exception as e:
223
+ return json({"message": str(e)}, status=500)
224
+
225
+ @bp.get("/training/<training_id>")
226
+ async def get_training(request: Request, training_id: str) -> response.HTTPResponse:
227
+ """Return the status of a training session."""
228
+ if training := trainings.get(training_id):
229
+ return json(
156
230
  {
157
- "training_id": training.training_id,
231
+ "training_id": training_id,
158
232
  "assistant_id": training.assistant_id,
159
233
  "client_id": training.client_id,
160
234
  "progress": training.progress,
161
235
  "status": training.status,
162
236
  }
163
- for training in trainings.values()
164
- ],
165
- }
166
- )
167
-
237
+ )
238
+ else:
239
+ return json({"message": "Training not found"}, status=404)
240
+
241
+ @bp.delete("/training/<training_id>")
242
+ async def stop_training(
243
+ request: Request, training_id: str
244
+ ) -> response.HTTPResponse:
245
+ # this is a no-op if the training is already done
246
+ if not (training := trainings.get(training_id)):
247
+ return json({"message": "Training session not found"}, status=404)
168
248
 
169
- @app.get("/training")
170
- async def get_training_list(request: Request) -> response.HTTPResponse:
171
- """Return a list of all training sessions for an assistant."""
172
- assistant_id = request.args.get("assistant_id")
173
- sessions = [
174
- {
175
- "training_id": session.training_id,
176
- "assistant_id": session.assistant_id,
177
- "client_id": session.client_id,
178
- "training_runtime": "self",
179
- "status": session.status,
180
- "bot_config": None,
181
- "logs": None,
182
- "metadata": None,
183
- "model": None,
184
- "runtime_metadata": None,
185
- }
186
- for session in trainings.values()
187
- if session.assistant_id == assistant_id
188
- ]
189
- return json({"training_sessions": sessions, "total_number": len(sessions)})
190
-
191
-
192
- @app.post("/training")
193
- async def start_training(request: Request) -> response.HTTPResponse:
194
- """Start a new training session."""
195
- data = request.json
196
- training_id: Optional[str] = data.get("id")
197
- assistant_id: Optional[str] = data.get("assistant_id")
198
- client_id: Optional[str] = data.get("client_id")
199
-
200
- if training_id in trainings:
201
- # fail, because there apparently is already a training with this id
202
- return json({"message": "Training with this id already exists"}, status=409)
203
-
204
- if not assistant_id:
205
- return json({"message": "Assistant id is required"}, status=400)
206
-
207
- if not training_id:
208
- return json({"message": "Training id is required"}, status=400)
209
-
210
- try:
211
- training_session = run_training(
212
- training_id=training_id,
213
- assistant_id=assistant_id,
214
- client_id=client_id,
215
- data=data,
216
- )
217
- trainings[training_id] = training_session
249
+ terminate_training(training)
218
250
  return json({"training_id": training_id})
219
- except Exception as e:
220
- return json({"message": str(e)}, status=500)
221
-
222
-
223
- @app.get("/training/<training_id>")
224
- async def get_training(request: Request, training_id: str) -> response.HTTPResponse:
225
- """Return the status of a training session."""
226
- if training := trainings.get(training_id):
227
- return json(
228
- {
229
- "training_id": training_id,
230
- "assistant_id": training.assistant_id,
231
- "client_id": training.client_id,
232
- "progress": training.progress,
233
- "status": training.status,
234
- }
235
- )
236
- else:
237
- return json({"message": "Training not found"}, status=404)
238
-
239
-
240
- @app.delete("/training/<training_id>")
241
- async def stop_training(request: Request, training_id: str) -> response.HTTPResponse:
242
- # this is a no-op if the training is already done
243
- if not (training := trainings.get(training_id)):
244
- return json({"message": "Training session not found"}, status=404)
245
251
 
246
- terminate_training(training)
247
- return json({"training_id": training_id})
248
-
249
-
250
- @app.get("/training/<training_id>/download_url")
251
- async def get_training_download_url(
252
- request: Request, training_id: str
253
- ) -> response.HTTPResponse:
254
- # Provide a URL for downloading the training log
255
- # check object key that is passed in as a query parameter
256
- key = request.args.get("object_key")
257
- if "model.tar.gz" in key:
258
- return get_training_model_url(request, training_id)
259
- return get_log_url(request, training_id)
252
+ @bp.get("/training/<training_id>/download_url")
253
+ async def get_training_download_url(
254
+ request: Request, training_id: str
255
+ ) -> response.HTTPResponse:
256
+ # Provide a URL for downloading the training log
257
+ # check object key that is passed in as a query parameter
258
+ key = request.args.get("object_key")
259
+ if "model.tar.gz" in key:
260
+ return get_training_model_url(request, training_id)
261
+ return get_log_url(request, training_id)
262
+
263
+ @bp.post("/bot")
264
+ async def start_bot(request: Request) -> response.HTTPResponse:
265
+ data = request.json
266
+ deployment_id: Optional[str] = data.get("deployment_id")
267
+ assumed_model_path: Optional[str] = data.get("model_path")
268
+
269
+ if deployment_id in running_bots:
270
+ # fail, because there apparently is already a bot running with this id
271
+ return json(
272
+ {"message": "Bot with this deployment id already exists"}, status=409
273
+ )
274
+
275
+ if not deployment_id:
276
+ return json({"message": "Deployment id is required"}, status=400)
277
+
278
+ if not assumed_model_path:
279
+ return json({"message": "Model path is required"}, status=400)
280
+
281
+ training_id = assumed_model_path.split("/")[-3]
282
+ training_base_path = train_path(training_id)
283
+ if not os.path.exists(f"{training_base_path}/models"):
284
+ return json(
285
+ {"message": "Model not found, for the given training id"},
286
+ status=404,
287
+ )
288
+
289
+ base_url_path = base_server_url(request)
290
+ try:
291
+ bot_session = run_bot(deployment_id, training_base_path, base_url_path)
292
+ running_bots[deployment_id] = bot_session
293
+ return json(
294
+ {
295
+ "deployment_id": deployment_id,
296
+ "status": bot_session.status,
297
+ "url": bot_session.url,
298
+ }
299
+ )
300
+ except Exception as e:
301
+ return json({"message": str(e)}, status=500)
260
302
 
303
+ @bp.delete("/bot/<deployment_id>")
304
+ async def stop_bot(request: Request, deployment_id: str) -> response.HTTPResponse:
305
+ bot = running_bots.get(deployment_id)
306
+ if bot is None:
307
+ return json({"message": "Bot not found"}, status=404)
261
308
 
262
- @app.post("/bot")
263
- async def start_bot(request: Request) -> response.HTTPResponse:
264
- data = request.json
265
- deployment_id: Optional[str] = data.get("deployment_id")
266
- assumed_model_path: Optional[str] = data.get("model_path")
309
+ terminate_bot(bot)
267
310
 
268
- if deployment_id in running_bots:
269
- # fail, because there apparently is already a bot running with this id
270
311
  return json(
271
- {"message": "Bot with this deployment id already exists"}, status=409
312
+ {"deployment_id": deployment_id, "status": bot.status, "url": bot.url}
272
313
  )
273
314
 
274
- if not deployment_id:
275
- return json({"message": "Deployment id is required"}, status=400)
315
+ @bp.get("/bot/<deployment_id>")
316
+ async def get_bot(request: Request, deployment_id: str) -> response.HTTPResponse:
317
+ bot = running_bots.get(deployment_id)
318
+ if bot is None:
319
+ return json({"message": "Bot not found"}, status=404)
276
320
 
277
- if not assumed_model_path:
278
- return json({"message": "Model path is required"}, status=400)
279
-
280
- training_id = assumed_model_path.split("/")[-3]
281
- training_base_path = train_path(training_id)
282
- if not os.path.exists(f"{training_base_path}/models"):
283
- return json(
284
- {"message": "Model not found, for the given training id"},
285
- status=404,
286
- )
287
-
288
- base_url_path = f"{request.scheme}://{request.host}"
289
- try:
290
- bot_session = run_bot(deployment_id, training_base_path, base_url_path)
291
- running_bots[deployment_id] = bot_session
292
321
  return json(
293
322
  {
294
323
  "deployment_id": deployment_id,
295
- "status": bot_session.status,
296
- "url": bot_session.url,
324
+ "status": bot.status,
325
+ "url": bot.url,
297
326
  }
298
327
  )
299
- except Exception as e:
300
- return json({"message": str(e)}, status=500)
301
-
302
-
303
- @app.delete("/bot/<deployment_id>")
304
- async def stop_bot(request: Request, deployment_id: str) -> response.HTTPResponse:
305
- bot = running_bots.get(deployment_id)
306
- if bot is None:
307
- return json({"message": "Bot not found"}, status=404)
308
-
309
- terminate_bot(bot)
310
-
311
- return json({"deployment_id": deployment_id, "status": bot.status, "url": bot.url})
312
-
313
-
314
- @app.get("/bot/<deployment_id>")
315
- async def get_bot(request: Request, deployment_id: str) -> response.HTTPResponse:
316
- bot = running_bots.get(deployment_id)
317
- if bot is None:
318
- return json({"message": "Bot not found"}, status=404)
319
-
320
- return json(
321
- {
322
- "deployment_id": deployment_id,
323
- "status": bot.status,
324
- "url": bot.url,
325
- }
326
- )
327
-
328
-
329
- @app.get("/bot/<deployment_id>/download_url")
330
- async def get_bot_download_url(
331
- request: Request, deployment_id: str
332
- ) -> response.HTTPResponse:
333
- return get_log_url(request, deployment_id)
334
-
335
-
336
- @app.get("/bot/<deployment_id>/logs")
337
- async def get_bot_logs(request: Request, deployment_id: str) -> response.HTTPResponse:
338
- return get_log_url(request, deployment_id)
339
-
340
-
341
- @app.get("/bot")
342
- async def list_bots(request: Request) -> response.HTTPResponse:
343
- bots = [
344
- {
345
- "deployment_id": bot.deployment_id,
346
- "status": bot.status,
347
- "url": bot.url,
348
- }
349
- for bot in running_bots.values()
350
- ]
351
- return json({"deployment_sessions": bots, "total_number": len(bots)})
352
-
353
-
354
- @app.route("/logs/<path:path>")
355
- async def get_training_logs(request: Request, path: str) -> response.HTTPResponse:
356
- try:
357
- headers = {"Content-Disposition": 'attachment; filename="log.txt"'}
358
- return await response.file(
359
- os.path.join(logs_base_path(), path), headers=headers
360
- )
361
- except NotFound:
362
- return json({"message": "Log not found"}, status=404)
363
-
364
-
365
- @app.route("/models/<path:path>")
366
- async def send_model(request: Request, path: str) -> response.HTTPResponse:
367
- try:
368
- return await response.file(models_path(path))
369
- except NotFound:
370
- return json({"message": "Model not found"}, status=404)
371
-
372
-
373
- @sio.on("connect")
374
- async def socketio_websocket_traffic(
375
- sid: str, environ: Dict, auth: Optional[Dict]
376
- ) -> bool:
377
- """Bridge websockets between user chat socket and bot server."""
378
- structlogger.debug("model_runner.user_connected", sid=sid)
379
- deployment_id = auth.get("deployment_id") if auth else None
380
-
381
- if deployment_id is None:
382
- structlogger.error("model_runner.bot_no_deployment_id", sid=sid)
383
- return False
384
-
385
- bot = running_bots.get(deployment_id)
386
- if bot is None:
387
- structlogger.error("model_runner.bot_not_found", deployment_id=deployment_id)
388
- return False
389
-
390
- client = await create_bridge_client(sio, bot.internal_url, sid, deployment_id)
391
-
392
- if client.sid is not None:
393
- structlogger.debug(
394
- "model_runner.bot_connection_established", deployment_id=deployment_id
395
- )
396
- socket_proxy_clients[sid] = client
397
- return True
398
- else:
399
- structlogger.error(
400
- "model_runner.bot_connection_failed", deployment_id=deployment_id
401
- )
402
- return False
403
-
404
-
405
- @sio.on("disconnect")
406
- async def disconnect(sid: str) -> None:
407
- structlogger.debug("model_runner.bot_disconnect", sid=sid)
408
- if sid in socket_proxy_clients:
409
- await socket_proxy_clients[sid].disconnect()
410
- del socket_proxy_clients[sid]
411
328
 
412
-
413
- @sio.on("*")
414
- async def handle_message(event: str, sid: str, data: Dict[str, Any]) -> None:
415
- # bridge both, incoming messages to the bot_url but also
416
- # send the response back to the client. both need to happen
417
- # in parallel in an async way
418
-
419
- client = socket_proxy_clients.get(sid)
420
- if client is None:
421
- structlogger.error("model_runner.bot_not_connected", sid=sid)
422
- return
423
-
424
- await client.emit(event, data)
329
+ @bp.get("/bot/<deployment_id>/download_url")
330
+ async def get_bot_download_url(
331
+ request: Request, deployment_id: str
332
+ ) -> response.HTTPResponse:
333
+ return get_log_url(request, deployment_id)
334
+
335
+ @bp.get("/bot/<deployment_id>/logs")
336
+ async def get_bot_logs(
337
+ request: Request, deployment_id: str
338
+ ) -> response.HTTPResponse:
339
+ return get_log_url(request, deployment_id)
340
+
341
+ @bp.get("/bot")
342
+ async def list_bots(request: Request) -> response.HTTPResponse:
343
+ bots = [
344
+ {
345
+ "deployment_id": bot.deployment_id,
346
+ "status": bot.status,
347
+ "url": bot.url,
348
+ }
349
+ for bot in running_bots.values()
350
+ ]
351
+ return json({"deployment_sessions": bots, "total_number": len(bots)})
352
+
353
+ return bp
354
+
355
+
356
+ def external_blueprint() -> Blueprint:
357
+ """Create a blueprint for the model manager API."""
358
+ from rasa.core.channels.socketio import SocketBlueprint
359
+
360
+ sio = AsyncServer(async_mode="sanic", cors_allowed_origins=[])
361
+ bp = SocketBlueprint(sio, "", "model_api_external")
362
+
363
+ @bp.route("/logs/<path:path>")
364
+ async def get_training_logs(request: Request, path: str) -> response.HTTPResponse:
365
+ try:
366
+ headers = {"Content-Disposition": 'attachment; filename="log.txt"'}
367
+ return await response.file(
368
+ os.path.join(logs_base_path(), path), headers=headers
369
+ )
370
+ except NotFound:
371
+ return json({"message": "Log not found"}, status=404)
372
+
373
+ @bp.route("/models/<path:path>")
374
+ async def send_model(request: Request, path: str) -> response.HTTPResponse:
375
+ try:
376
+ return await response.file(models_path(path))
377
+ except NotFound:
378
+ return json({"message": "Model not found"}, status=404)
379
+
380
+ @sio.on("connect")
381
+ async def socketio_websocket_traffic(
382
+ sid: str, environ: Dict, auth: Optional[Dict]
383
+ ) -> bool:
384
+ """Bridge websockets between user chat socket and bot server."""
385
+ structlogger.debug("model_runner.user_connected", sid=sid)
386
+ deployment_id = auth.get("deployment_id") if auth else None
387
+
388
+ if deployment_id is None:
389
+ structlogger.error("model_runner.bot_no_deployment_id", sid=sid)
390
+ return False
391
+
392
+ bot = running_bots.get(deployment_id)
393
+ if bot is None:
394
+ structlogger.error(
395
+ "model_runner.bot_not_found", deployment_id=deployment_id
396
+ )
397
+ return False
398
+
399
+ client = await create_bridge_client(sio, bot.internal_url, sid, deployment_id)
400
+
401
+ if client.sid is not None:
402
+ structlogger.debug(
403
+ "model_runner.bot_connection_established", deployment_id=deployment_id
404
+ )
405
+ socket_proxy_clients[sid] = client
406
+ return True
407
+ else:
408
+ structlogger.error(
409
+ "model_runner.bot_connection_failed", deployment_id=deployment_id
410
+ )
411
+ return False
412
+
413
+ @sio.on("disconnect")
414
+ async def disconnect(sid: str) -> None:
415
+ structlogger.debug("model_runner.bot_disconnect", sid=sid)
416
+ if sid in socket_proxy_clients:
417
+ await socket_proxy_clients[sid].disconnect()
418
+ del socket_proxy_clients[sid]
419
+
420
+ @sio.on("*")
421
+ async def handle_message(event: str, sid: str, data: Dict[str, Any]) -> None:
422
+ # bridge both, incoming messages to the bot_url but also
423
+ # send the response back to the client. both need to happen
424
+ # in parallel in an async way
425
+
426
+ client = socket_proxy_clients.get(sid)
427
+ if client is None:
428
+ structlogger.error("model_runner.bot_not_connected", sid=sid)
429
+ return
430
+
431
+ await client.emit(event, data)
432
+
433
+ return bp
rasa/model_service.py CHANGED
@@ -1,9 +1,12 @@
1
1
  import os
2
2
  import logging
3
3
 
4
+ from sanic import Sanic
4
5
  import structlog
5
6
 
7
+ from rasa.core.utils import list_routes
6
8
  from rasa.model_manager import model_api
9
+ from rasa.model_manager.config import SERVER_BASE_URL
7
10
  from rasa.utils.common import configure_logging_and_warnings
8
11
  import rasa.utils.licensing
9
12
 
@@ -12,6 +15,16 @@ structlogger = structlog.get_logger()
12
15
  MODEL_SERVICE_PORT = 8000
13
16
 
14
17
 
18
+ def url_prefix_from_base_url() -> str:
19
+ """Return the path prefix from the base URL."""
20
+ if SERVER_BASE_URL:
21
+ from urllib.parse import urlparse
22
+
23
+ return urlparse(SERVER_BASE_URL).path
24
+
25
+ return ""
26
+
27
+
15
28
  def main() -> None:
16
29
  """Start the Rasa Model Manager server.
17
30
 
@@ -36,7 +49,17 @@ def main() -> None:
36
49
  structlogger.debug("model_training.starting_server", port=MODEL_SERVICE_PORT)
37
50
  structlogger.debug("model_running.starting_server", port=MODEL_SERVICE_PORT)
38
51
 
39
- model_api.app.run(host="0.0.0.0", port=MODEL_SERVICE_PORT, legacy=True)
52
+ url_prefix = url_prefix_from_base_url()
53
+ # configure the sanice application
54
+ app = Sanic("RasaModelService")
55
+ app.add_task(model_api.continuously_update_process_status)
56
+ app.blueprint(model_api.external_blueprint(), url_prefix=url_prefix)
57
+ app.blueprint(model_api.internal_blueprint())
58
+
59
+ # list all routes
60
+ list_routes(app)
61
+
62
+ app.run(host="0.0.0.0", port=MODEL_SERVICE_PORT, legacy=True)
40
63
 
41
64
 
42
65
  if __name__ == "__main__":
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.10.7.dev1"
3
+ __version__ = "3.10.7.dev3"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: rasa-pro
3
- Version: 3.10.7.dev1
3
+ Version: 3.10.7.dev3
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
  Home-page: https://rasa.com
6
6
  Keywords: nlp,machine-learning,machine-learning-library,bot,bots,botkit,rasa conversational-agents,conversational-ai,chatbot,chatbot-framework,bot-framework
@@ -251,7 +251,7 @@ rasa/core/channels/rasa_chat.py,sha256=XGZ7QLyQHhB-m7EjetDNEBSjAa2mEFqU-e-FuS9z3
251
251
  rasa/core/channels/rest.py,sha256=zeUAKr0UyeJVt_UFfri7GAYcGzTdG-mBwwN3zLVzjpc,7129
252
252
  rasa/core/channels/rocketchat.py,sha256=HWOMxXLuwadYEYIMMP-z6RqAJzMGZDLklpgqLOipXF0,5998
253
253
  rasa/core/channels/slack.py,sha256=3b8OZQ_gih5XBwhQ1q4BbBUC1SCAPaO9AoJEn2NaoQE,24405
254
- rasa/core/channels/socketio.py,sha256=k0b6aWE8gqhXBaLN7Sa5qaxqrWEFqf95ZeN-fX9bhPA,10675
254
+ rasa/core/channels/socketio.py,sha256=Lo342YwWm2O5PciEUXAXA3NRJQ5yi3ZXosRXItyzsc4,10807
255
255
  rasa/core/channels/telegram.py,sha256=XvzU7KAgjmRcu2vUJ-5L2eYAAuYBqtcYlhq1Jo6kLns,10649
256
256
  rasa/core/channels/twilio.py,sha256=c63uFLVKaK4Fj8MAn9BSQtxiV_Ezq4POezHo8zWIoiw,5938
257
257
  rasa/core/channels/twilio_voice.py,sha256=SrtNZOk3Yuk_QuUk45Hadj3PGvmwD0jeSzF90GbiUV0,13244
@@ -483,13 +483,13 @@ rasa/markers/upload.py,sha256=Ot1s_O-CEIB9c4CKUlfOldiJo92pdqxFUHOPCU7E_NU,2518
483
483
  rasa/markers/validate.py,sha256=YypXKRS87xxrMMEz9HpAQzYJUwg0OPbczMXBRNjlJq4,709
484
484
  rasa/model.py,sha256=GH1-N6Po3gL3nwfa9eGoN2bMRNMrn4f3mi17-osW3T0,3491
485
485
  rasa/model_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
486
- rasa/model_manager/config.py,sha256=CgloLGxk5FtPYxFK9TZPfD9nYdMEJwI53WrBwD4RMtE,211
487
- rasa/model_manager/model_api.py,sha256=vAyysrHf6zt3OeayAS7d_URZt97XAxRrIzRVKWEcTzU,13730
486
+ rasa/model_manager/config.py,sha256=LCzRoV78bLX_FREW7hfQLsQepupoTs1m-CnK3u_D01s,349
487
+ rasa/model_manager/model_api.py,sha256=_rj1VfYRbFj7TbPG1mryR_SVXLgTu6gVfXAiEdbbqwM,15187
488
488
  rasa/model_manager/runner_service.py,sha256=aMupR7t0EEy2yOGT5cR_6kluApnwOiiGkjyIVYxdsMk,5553
489
489
  rasa/model_manager/socket_bridge.py,sha256=f-dhsV8xYaENXVZT5OOzkMdpSPPPxFEzsQqNJAuktxQ,1390
490
490
  rasa/model_manager/trainer_service.py,sha256=7LP_EsLQt-P2oqO1AgqbgIcTC515eCW58BamDkDjJjw,7856
491
491
  rasa/model_manager/utils.py,sha256=-DJUcgRiyQZsszlOYlww-OGaAof8CIopbsOxZmeZ9NY,819
492
- rasa/model_service.py,sha256=FD7cvLpn-DKysdPZ_TyGYaX9hOfkQXbUG7eiIGyhdwo,1139
492
+ rasa/model_service.py,sha256=x6vFrxc_VH8dh1I5Tkw2cbUimbqoxpiFQKpcem9tSg8,1818
493
493
  rasa/model_testing.py,sha256=h0QUpJu6p_TDse3aHjCfYwI6OGH47b3Iuo5Ot0HQADM,14959
494
494
  rasa/model_training.py,sha256=sFdrizWu8JZZDGrMjI6_0OaY9dK-msOShWpgOOCGZzg,20601
495
495
  rasa/nlu/__init__.py,sha256=D0IYuTK_ZQ_F_9xsy0bXxVCAtU62Fzvp8S7J9tmfI_c,123
@@ -727,9 +727,9 @@ rasa/utils/train_utils.py,sha256=f1NWpp5y6al0dzoQyyio4hc4Nf73DRoRSHDzEK6-C4E,212
727
727
  rasa/utils/url_tools.py,sha256=JQcHL2aLqLHu82k7_d9imUoETCm2bmlHaDpOJ-dKqBc,1218
728
728
  rasa/utils/yaml.py,sha256=KjbZq5C94ZP7Jdsw8bYYF7HASI6K4-C_kdHfrnPLpSI,2000
729
729
  rasa/validator.py,sha256=HM0ZIWjo3JRt2FMIfgNI_s932OABOSXkflm-rFTNkvY,62608
730
- rasa/version.py,sha256=mWwXjlqLz4H68SGfZTsjjqVjCUyp6IRXez6QPLV9UeQ,122
731
- rasa_pro-3.10.7.dev1.dist-info/METADATA,sha256=kunqQOTbfLTTvXrxMPaydYCd0iZ2bNYG5YmnDxZuO6A,28217
732
- rasa_pro-3.10.7.dev1.dist-info/NOTICE,sha256=7HlBoMHJY9CL2GlYSfTQ-PZsVmLmVkYmMiPlTjhuCqA,218
733
- rasa_pro-3.10.7.dev1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
734
- rasa_pro-3.10.7.dev1.dist-info/entry_points.txt,sha256=ckJ2SfEyTPgBqj_I6vm_tqY9dZF_LAPJZA335Xp0Q9U,43
735
- rasa_pro-3.10.7.dev1.dist-info/RECORD,,
730
+ rasa/version.py,sha256=DlTtggo2Cpv3vfmdgNMdjJQ8QS2-yQJIFm9LME4uBLY,122
731
+ rasa_pro-3.10.7.dev3.dist-info/METADATA,sha256=JUZzFADQy5AKkpzwfIMNb2968TjBMJDEpzRFeFklz6A,28217
732
+ rasa_pro-3.10.7.dev3.dist-info/NOTICE,sha256=7HlBoMHJY9CL2GlYSfTQ-PZsVmLmVkYmMiPlTjhuCqA,218
733
+ rasa_pro-3.10.7.dev3.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
734
+ rasa_pro-3.10.7.dev3.dist-info/entry_points.txt,sha256=ckJ2SfEyTPgBqj_I6vm_tqY9dZF_LAPJZA335Xp0Q9U,43
735
+ rasa_pro-3.10.7.dev3.dist-info/RECORD,,