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.
- rasa/core/channels/socketio.py +5 -1
- rasa/model_manager/config.py +6 -1
- rasa/model_manager/model_api.py +288 -279
- rasa/model_service.py +24 -1
- rasa/version.py +1 -1
- {rasa_pro-3.10.7.dev1.dist-info → rasa_pro-3.10.7.dev3.dist-info}/METADATA +1 -1
- {rasa_pro-3.10.7.dev1.dist-info → rasa_pro-3.10.7.dev3.dist-info}/RECORD +10 -10
- {rasa_pro-3.10.7.dev1.dist-info → rasa_pro-3.10.7.dev3.dist-info}/NOTICE +0 -0
- {rasa_pro-3.10.7.dev1.dist-info → rasa_pro-3.10.7.dev3.dist-info}/WHEEL +0 -0
- {rasa_pro-3.10.7.dev1.dist-info → rasa_pro-3.10.7.dev3.dist-info}/entry_points.txt +0 -0
rasa/core/channels/socketio.py
CHANGED
|
@@ -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
|
-
|
|
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
|
|
rasa/model_manager/config.py
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import sys
|
|
2
|
+
import os
|
|
2
3
|
|
|
3
|
-
SERVER_BASE_WORKING_DIRECTORY =
|
|
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
|
rasa/model_manager/model_api.py
CHANGED
|
@@ -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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
134
|
-
|
|
135
|
-
|
|
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
|
-
@
|
|
142
|
-
async def health(request: Request) -> response.HTTPResponse:
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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":
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
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
|
-
|
|
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
|
-
{"
|
|
312
|
+
{"deployment_id": deployment_id, "status": bot.status, "url": bot.url}
|
|
272
313
|
)
|
|
273
314
|
|
|
274
|
-
|
|
275
|
-
|
|
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":
|
|
296
|
-
"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
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
return
|
|
423
|
-
|
|
424
|
-
|
|
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
|
-
|
|
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,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: rasa-pro
|
|
3
|
-
Version: 3.10.7.
|
|
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=
|
|
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=
|
|
487
|
-
rasa/model_manager/model_api.py,sha256=
|
|
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=
|
|
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=
|
|
731
|
-
rasa_pro-3.10.7.
|
|
732
|
-
rasa_pro-3.10.7.
|
|
733
|
-
rasa_pro-3.10.7.
|
|
734
|
-
rasa_pro-3.10.7.
|
|
735
|
-
rasa_pro-3.10.7.
|
|
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,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|