rasa-pro 3.13.0a1.dev5__py3-none-any.whl → 3.13.0a1.dev7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of rasa-pro might be problematic. Click here for more details.

Files changed (49) hide show
  1. rasa/builder/README.md +120 -0
  2. rasa/builder/config.py +69 -0
  3. rasa/builder/create_openai_vector_store.py +204 -45
  4. rasa/builder/exceptions.py +49 -0
  5. rasa/builder/llm_helper_prompt.jinja2 +245 -0
  6. rasa/builder/llm_service.py +327 -0
  7. rasa/builder/logging_utils.py +51 -0
  8. rasa/builder/main.py +61 -0
  9. rasa/builder/models.py +174 -0
  10. rasa/builder/project_generator.py +264 -0
  11. rasa/builder/service.py +447 -0
  12. rasa/builder/{skill_to_bot_prompt.jinja → skill_to_bot_prompt.jinja2} +10 -4
  13. rasa/builder/training_service.py +123 -0
  14. rasa/builder/validation_service.py +79 -0
  15. rasa/cli/project_templates/finance/config.yml +17 -0
  16. rasa/cli/project_templates/finance/credentials.yml +33 -0
  17. rasa/cli/project_templates/finance/data/flows/transfer_money.yml +5 -0
  18. rasa/cli/project_templates/finance/data/patterns/pattern_session_start.yml +7 -0
  19. rasa/cli/project_templates/finance/domain.yml +7 -0
  20. rasa/cli/project_templates/finance/endpoints.yml +58 -0
  21. rasa/cli/project_templates/plain/config.yml +17 -0
  22. rasa/cli/project_templates/plain/credentials.yml +33 -0
  23. rasa/cli/project_templates/plain/data/patterns/pattern_session_start.yml +7 -0
  24. rasa/cli/project_templates/plain/domain.yml +5 -0
  25. rasa/cli/project_templates/plain/endpoints.yml +58 -0
  26. rasa/cli/project_templates/telecom/config.yml +17 -0
  27. rasa/cli/project_templates/telecom/credentials.yml +33 -0
  28. rasa/cli/project_templates/telecom/data/flows/upgrade_contract.yml +5 -0
  29. rasa/cli/project_templates/telecom/data/patterns/pattern_session_start.yml +7 -0
  30. rasa/cli/project_templates/telecom/domain.yml +7 -0
  31. rasa/cli/project_templates/telecom/endpoints.yml +58 -0
  32. rasa/cli/scaffold.py +19 -3
  33. rasa/core/actions/action.py +5 -3
  34. rasa/core/channels/studio_chat.py +17 -3
  35. rasa/model_manager/model_api.py +1 -1
  36. rasa/model_manager/runner_service.py +1 -1
  37. rasa/model_manager/trainer_service.py +1 -1
  38. rasa/model_manager/utils.py +1 -29
  39. rasa/shared/core/domain.py +62 -15
  40. rasa/shared/core/flows/yaml_flows_io.py +16 -8
  41. rasa/telemetry.py +2 -1
  42. rasa/utils/io.py +27 -9
  43. rasa/version.py +1 -1
  44. {rasa_pro-3.13.0a1.dev5.dist-info → rasa_pro-3.13.0a1.dev7.dist-info}/METADATA +1 -1
  45. {rasa_pro-3.13.0a1.dev5.dist-info → rasa_pro-3.13.0a1.dev7.dist-info}/RECORD +48 -20
  46. rasa/builder/prompt_to_bot.py +0 -696
  47. {rasa_pro-3.13.0a1.dev5.dist-info → rasa_pro-3.13.0a1.dev7.dist-info}/NOTICE +0 -0
  48. {rasa_pro-3.13.0a1.dev5.dist-info → rasa_pro-3.13.0a1.dev7.dist-info}/WHEEL +0 -0
  49. {rasa_pro-3.13.0a1.dev5.dist-info → rasa_pro-3.13.0a1.dev7.dist-info}/entry_points.txt +0 -0
@@ -1,696 +0,0 @@
1
- import asyncio
2
- import collections
3
- import importlib
4
- import json
5
- import logging
6
- import sys
7
- import tempfile
8
- import traceback
9
- from copy import deepcopy
10
- from textwrap import dedent
11
- from typing import Any, Callable, Dict, List, Optional
12
-
13
- import importlib_resources
14
- import openai
15
- import structlog
16
- from jinja2 import Template
17
- from pydantic import BaseModel
18
- from sanic import Sanic, response
19
- from structlog.testing import capture_logs
20
-
21
- import rasa.core.utils
22
- from rasa.builder.llm_context import tracker_as_llm_context
23
- from rasa.cli.utils import validate_files
24
- from rasa.constants import PACKAGE_NAME
25
- from rasa.core import agent, channels
26
- from rasa.core.channels.channel import InputChannel
27
- from rasa.core.channels.studio_chat import StudioChatInput
28
- from rasa.core.utils import AvailableEndpoints, read_endpoints_from_path
29
- from rasa.model_training import train
30
- from rasa.server import configure_cors
31
- from rasa.shared.constants import DOMAIN_SCHEMA_FILE, RESPONSES_SCHEMA_FILE
32
- from rasa.shared.core.domain import Domain
33
- from rasa.shared.core.flows.yaml_flows_io import FLOWS_SCHEMA_FILE, YAMLFlowsReader
34
- from rasa.shared.core.trackers import DialogueStateTracker
35
- from rasa.shared.importers.importer import TrainingDataImporter
36
- from rasa.shared.importers.static import StaticTrainingDataImporter
37
- from rasa.shared.utils.io import read_json_file
38
- from rasa.shared.utils.yaml import (
39
- dump_obj_as_yaml_to_string,
40
- read_schema_file,
41
- read_yaml,
42
- read_yaml_file,
43
- )
44
- from rasa.utils.common import configure_logging_and_warnings
45
- from rasa.utils.log_utils import configure_structlog
46
- from rasa.utils.sanic_error_handler import register_custom_sanic_error_handler
47
-
48
- structlogger = structlog.get_logger()
49
-
50
- DEFAULT_SKILL_GENERATION_SYSTEM_PROMPT = importlib.resources.read_text(
51
- "rasa.builder",
52
- "skill_to_bot_prompt.jinja",
53
- )
54
-
55
- VECTOR_STORE_ID = "vs_685123376e288191a005b6b144d3026f"
56
-
57
-
58
- default_credentials_yaml = """
59
- studio_chat:
60
- user_message_evt: "user_message"
61
- bot_message_evt: "bot_message"
62
- session_persistence: true
63
- """
64
-
65
- # create a dict where we collect most recent logs. only collect the last 30 log lines
66
- # use a builtin type for this
67
- recent_logs = collections.deque(maxlen=30)
68
-
69
-
70
- def collecting_logs_processor(logger, log_level, event_dict):
71
- if log_level != logging.getLevelName(logging.DEBUG).lower():
72
- event_message = event_dict.get("event_info") or event_dict.get("event", "")
73
- recent_logs.append(f"[{log_level}] {event_message}")
74
-
75
- return event_dict
76
-
77
-
78
- class PromptRequest(BaseModel):
79
- prompt: str
80
- client_id: Optional[str] = None
81
-
82
-
83
- def default_credentials() -> Dict[str, Any]:
84
- return read_yaml(default_credentials_yaml)
85
-
86
-
87
- def default_endpoints() -> Dict[str, Any]:
88
- return read_yaml_file(
89
- str(
90
- importlib_resources.files(PACKAGE_NAME).joinpath(
91
- "cli/project_templates/default/endpoints.yml"
92
- )
93
- )
94
- )
95
-
96
-
97
- def default_config(assistant_id: str) -> Dict[str, Any]:
98
- base_config = read_yaml_file(
99
- str(
100
- importlib_resources.files(PACKAGE_NAME).joinpath(
101
- "cli/project_templates/default/config.yml"
102
- )
103
- )
104
- )
105
-
106
- base_config["assistant_id"] = assistant_id
107
-
108
- return base_config
109
-
110
-
111
- async def continuously_run_task(task: Callable, name: str) -> None:
112
- """Regularly run a task."""
113
- structlogger.debug("prompt_to_bot.continuously_run_task.started", name=name)
114
-
115
- while True:
116
- try:
117
- if asyncio.iscoroutinefunction(task):
118
- await task()
119
- else:
120
- task()
121
- except asyncio.exceptions.CancelledError:
122
- structlogger.debug(
123
- "prompt_to_bot.continuously_run_task.cancelled", name=name
124
- )
125
- break
126
- except Exception as e:
127
- structlogger.error(
128
- "prompt_to_bot.continuously_run_task.error", name=name, error=str(e)
129
- )
130
- finally:
131
- await asyncio.sleep(0.1)
132
-
133
-
134
- class PromptToBotService:
135
- def __init__(self):
136
- self.app = Sanic("PromptToBotService")
137
- self.app.ctx.agent = None
138
- self.input_channel = self.setup_input_channel()
139
- self.setup_routes(self.input_channel)
140
- self.max_retries = 5
141
- self.bot_files = {}
142
-
143
- configure_cors(self.app, cors_origins=["*"])
144
-
145
- def setup_input_channel(self) -> StudioChatInput:
146
- studio_chat_credentials = default_credentials().get(StudioChatInput.name())
147
- return StudioChatInput.from_credentials(credentials=studio_chat_credentials)
148
-
149
- def setup_routes(self, input_channel: InputChannel):
150
- self.app.add_route(
151
- self.handle_prompt_to_bot, "/api/prompt-to-bot", methods=["POST"]
152
- )
153
- self.app.add_route(self.get_bot_data, "/api/bot-data", methods=["GET"])
154
-
155
- self.app.add_route(self.update_bot_data, "/api/bot-data", methods=["PUT"])
156
-
157
- self.app.add_route(self.llm_builder, "/api/llm-builder", methods=["POST"])
158
-
159
- self.app.add_route(self.health, "/", methods=["GET"])
160
-
161
- input_channels = [input_channel]
162
- channels.channel.register(input_channels, self.app, route="/webhooks/")
163
-
164
- def health(self, request):
165
- return response.json({"status": "ok"})
166
-
167
- def importer_for_data(self) -> TrainingDataImporter:
168
- return TrainingDataImporter.wrap_in_builtins(
169
- [
170
- StaticTrainingDataImporter(
171
- domain=Domain.from_dict(
172
- read_yaml(self.bot_files.get("domain.yml", ""))
173
- ),
174
- flows=YAMLFlowsReader.read_from_string(
175
- self.bot_files.get("flows.yml", "")
176
- ),
177
- config=self.config_from_bot_data(),
178
- )
179
- ]
180
- )
181
-
182
- async def validate_rasa_project(self) -> Optional[str]:
183
- """Validate the Rasa project data."""
184
- was_sys_exit_called = {"value": False}
185
-
186
- def sys_exit_mock(code: int = 0):
187
- was_sys_exit_called["value"] = True
188
-
189
- # prevent sys.exit from being called
190
- original_exit = sys.exit
191
- # TODO: avoid sys exit in the validation functions in the first place
192
- sys.exit = sys_exit_mock
193
- try:
194
- training_data_importer = self.importer_for_data()
195
-
196
- with capture_logs() as cap_logs:
197
- validate_files(
198
- fail_on_warnings=False,
199
- max_history=None,
200
- importer=training_data_importer,
201
- )
202
-
203
- if was_sys_exit_called["value"]:
204
- structlogger.error(
205
- "prompt_to_bot.validate_rasa_project.failed.sys_exit",
206
- error_logs=cap_logs,
207
- )
208
- return json.dumps(
209
- [x for x in cap_logs if x.get("log_level") != "debug"]
210
- )
211
-
212
- return None
213
- except Exception as e:
214
- structlogger.error(
215
- "prompt_to_bot.validate_rasa_project.failed.exception",
216
- error=str(e),
217
- traceback=traceback.format_exc(),
218
- )
219
- return str(e)
220
- finally:
221
- sys.exit = original_exit
222
-
223
- async def handle_prompt_to_bot(self, request):
224
- try:
225
- prompt_data = PromptRequest(**request.json)
226
- config = default_config(prompt_data.client_id)
227
- # Generate Rasa project data with retries
228
- await self.generate_rasa_project_with_retries(
229
- prompt_data.prompt,
230
- config,
231
- self.max_retries,
232
- )
233
-
234
- self.app.ctx.agent = await self.train_and_load_agent()
235
-
236
- return response.json(
237
- {
238
- "bot_data": self.bot_files,
239
- "status": "success",
240
- }
241
- )
242
-
243
- except Exception as e:
244
- structlogger.error("prompt_to_bot.error", error=str(e))
245
- return response.json({"error": str(e)}, status=500)
246
-
247
- async def get_bot_data(self, request):
248
- return response.json(self.bot_files)
249
-
250
- async def update_bot_data(self, request):
251
- response = await request.respond(content_type="text/event-stream")
252
-
253
- def sse_event(event, data):
254
- return f"event: {event}\ndata: {json.dumps(data)}\n\n"
255
-
256
- # 1. Received
257
- await response.send(sse_event("received", {"status": "received"}))
258
-
259
- bot_data = request.json
260
- for file_name, file_content in bot_data.items():
261
- self.bot_files[file_name] = file_content
262
-
263
- # 2. Validating
264
- await response.send(sse_event("validating", {"status": "validating"}))
265
- try:
266
- await self.validate_rasa_project()
267
- await response.send(
268
- sse_event("validation_success", {"status": "validation_success"})
269
- )
270
- except Exception as e:
271
- structlogger.error(
272
- "prompt_to_bot.update_bot_data.validation_error",
273
- error=str(e),
274
- event_info="Failed to validate the Rasa project. Error: " + str(e),
275
- )
276
- await response.send(
277
- sse_event(
278
- "validation_error",
279
- {"status": "validation_error", "error": str(e)},
280
- )
281
- )
282
- await response.eof()
283
- return
284
-
285
- # 3. Training
286
- await response.send(sse_event("training", {"status": "training"}))
287
- try:
288
- self.app.ctx.agent = await self.train_and_load_agent()
289
- await response.send(sse_event("train_success", {"status": "train_success"}))
290
- except Exception as e:
291
- structlogger.error(
292
- "prompt_to_bot.update_bot_data.train_error",
293
- error=str(e),
294
- event_info="Failed to train the agent. Error: " + str(e),
295
- )
296
- await response.send(
297
- sse_event("train_error", {"status": "train_error", "error": str(e)})
298
- )
299
- await response.eof()
300
- return
301
-
302
- # 4. Done
303
- await response.send(
304
- sse_event("done", {"status": "done", "bot_data": self.bot_files})
305
- )
306
- await response.eof()
307
-
308
- def config_from_bot_data(self) -> Dict[str, Any]:
309
- return read_yaml(self.bot_files.get("config.yml", ""))
310
-
311
- def update_stored_bot_data(self, bot_data: Dict[str, Any], config: Dict[str, Any]):
312
- self.bot_files = {
313
- "domain.yml": dump_obj_as_yaml_to_string(bot_data["domain"]),
314
- "flows.yml": dump_obj_as_yaml_to_string(bot_data["flows"]),
315
- "config.yml": dump_obj_as_yaml_to_string(config),
316
- }
317
-
318
- async def generate_rasa_project_with_retries(
319
- self, base_prompt: str, config: Dict[str, Any], max_retry_count: int = 5
320
- ) -> Dict[str, Any]:
321
- """Generate Rasa project data with retry logic."""
322
- initial_messages = self.prompt_messages(base_prompt)
323
-
324
- async def _generate(messages: List[Dict[str, Any]], tries_left: int):
325
- rasa_project_data = await self.generate_rasa_project(messages)
326
- self.update_stored_bot_data(rasa_project_data, config)
327
-
328
- # write the data to a file
329
- with open(f"rasa_project_{tries_left}.json", "w") as f:
330
- json.dump(rasa_project_data, f)
331
-
332
- structlogger.info(
333
- "prompt_to_bot.generate_rasa_project_with_retries.generated_project",
334
- tries_left=tries_left,
335
- )
336
-
337
- try:
338
- validation_error = await self.validate_rasa_project()
339
-
340
- if validation_error:
341
- structlogger.error(
342
- "prompt_to_bot.generate_rasa_project_with_retries.validation_error",
343
- validation_error=validation_error,
344
- )
345
- raise Exception(validation_error)
346
-
347
- structlogger.info(
348
- "prompt_to_bot.generate_rasa_project_with_retries.validation_success",
349
- tries_left=tries_left,
350
- )
351
-
352
- return rasa_project_data
353
- except Exception as e:
354
- structlogger.error(
355
- "prompt_to_bot.generate_rasa_project_with_retries.error",
356
- error=str(e),
357
- tries_left=tries_left,
358
- )
359
-
360
- if tries_left <= 0:
361
- raise Exception(
362
- f"Failed to generate valid Rasa project after "
363
- f"{max_retry_count} attempts"
364
- )
365
-
366
- # Use error to improve the prompt
367
- messages = messages + [
368
- {
369
- "role": "assistant",
370
- "content": json.dumps(rasa_project_data),
371
- },
372
- {
373
- "role": "user",
374
- "content": dedent(f"""
375
- Previous attempt failed with error: {e!s}
376
-
377
- Please fix the issues and generate a valid Rasa project.
378
- """),
379
- },
380
- ]
381
-
382
- return await _generate(messages, tries_left - 1)
383
-
384
- return await _generate(initial_messages, max_retry_count)
385
-
386
- def prompt_messages(self, prompt: str) -> List[Dict[str, Any]]:
387
- system_prompt = Template(DEFAULT_SKILL_GENERATION_SYSTEM_PROMPT).render(
388
- prompt=prompt
389
- )
390
-
391
- return [
392
- {"role": "system", "content": system_prompt},
393
- ]
394
-
395
- async def generate_rasa_project(
396
- self, messages: List[Dict[str, Any]]
397
- ) -> Dict[str, Any]:
398
- """Generate Rasa project data using LLM."""
399
- schema_file = str(
400
- importlib_resources.files(PACKAGE_NAME).joinpath(FLOWS_SCHEMA_FILE)
401
- )
402
-
403
- # TODO: clean up the schema
404
- flows_schema = deepcopy(read_json_file(schema_file))
405
-
406
- del flows_schema["$defs"]["flow"]["properties"]["nlu_trigger"]
407
-
408
- # TODO: restrict the domain schema to only the properties that are
409
- # needed for the CALM bot
410
- domain_schema = deepcopy(
411
- read_schema_file(DOMAIN_SCHEMA_FILE, PACKAGE_NAME, False)
412
- )
413
-
414
- # not needed in calm
415
- del domain_schema["mapping"]["intents"]
416
- del domain_schema["mapping"]["entities"]
417
- del domain_schema["mapping"]["forms"]
418
-
419
- # don't think the llm needs to configure these
420
- del domain_schema["mapping"]["config"]
421
- del domain_schema["mapping"]["session_config"]
422
-
423
- # don't work, llm tends to pick from_intent or something like that
424
- del domain_schema["mapping"]["slots"]["mapping"]["regex;([A-Za-z]+)"][
425
- "mapping"
426
- ]["mappings"]
427
- # also creates issues...
428
- del domain_schema["mapping"]["slots"]["mapping"]["regex;([A-Za-z]+)"][
429
- "mapping"
430
- ]["validation"]
431
-
432
- # pull in the responses schema
433
- domain_schema["mapping"]["responses"] = read_schema_file(
434
- RESPONSES_SCHEMA_FILE, PACKAGE_NAME, False
435
- )["schema;responses"]
436
-
437
- client = openai.AsyncOpenAI()
438
- response = await client.chat.completions.create(
439
- model="gpt-4.1-2025-04-14",
440
- messages=messages,
441
- temperature=0.7,
442
- response_format={
443
- "type": "json_schema",
444
- "json_schema": {
445
- "name": "rasa_project",
446
- "schema": {
447
- "type": "object",
448
- "properties": {
449
- "domain": domain_schema,
450
- "flows": flows_schema,
451
- },
452
- "required": ["domain", "flows"],
453
- },
454
- },
455
- },
456
- )
457
-
458
- try:
459
- return json.loads(response.choices[0].message.content)
460
- except json.JSONDecodeError:
461
- raise Exception("LLM response was not valid JSON")
462
-
463
- async def generate_chat_bot_context(self) -> str:
464
- """Generate a chat bot context."""
465
- if self.app.ctx.agent and self.input_channel.latest_tracker_session_id:
466
- tracker: Optional[
467
- DialogueStateTracker
468
- ] = await self.app.ctx.agent.tracker_store.retrieve(
469
- self.input_channel.latest_tracker_session_id
470
- )
471
- return tracker_as_llm_context(tracker)
472
- else:
473
- return tracker_as_llm_context(None)
474
-
475
- def format_chat_dump(self, user_chat_history: List[Dict[str, Any]]) -> str:
476
- """Format the chat dump for the LLM."""
477
- result = ""
478
- for message in user_chat_history:
479
- if message.get("type") == "user":
480
- result += f"User: {message.get('content')}\n"
481
- else:
482
- for part in message.get("content", []):
483
- if part.get("type") == "text":
484
- result += f"Assistant: {part.get('text')}\n"
485
- return result
486
-
487
- async def llm_builder(self, request):
488
- chat_bot_context = await self.generate_chat_bot_context()
489
- recent_logs_context = "\n".join(recent_logs)
490
- chat_bot_files_context = json.dumps(self.bot_files)
491
- user_chat_history = request.json.get("messages", [])
492
- chat_dump = self.format_chat_dump(user_chat_history)
493
-
494
- client = openai.AsyncOpenAI()
495
-
496
- results = await client.vector_stores.search(
497
- vector_store_id=VECTOR_STORE_ID,
498
- query=chat_dump,
499
- max_num_results=10,
500
- rewrite_query=True,
501
- )
502
-
503
- formatted_results = self.format_results(results.data)
504
-
505
- prompt = dedent(f"""
506
- You are a helpful assistant that helps users building chatbots using Rasa.
507
- Do not mention Rasa Studio - only ever refer to Rasa Pro.
508
- You are given the user question as well as context about the user's project.
509
- You need to generate a response using content blocks.
510
-
511
- The content blocks should be in the following format:
512
- {{
513
- "type": "text",
514
- "text": "Great question, you can take a look at the following resource:"
515
- }}
516
- The type can be one of the following: text, link, file, code.
517
- When using a block of type `text`, the `text` attribute can contain markdown.
518
- Use markdown to format the text when needed.
519
- The text is the text of the content block.
520
-
521
- Code blocks:
522
- {{
523
- "type": "code",
524
- "text": "- action: utter_greet\n next: END\n",
525
- "language": "yaml"
526
- }}
527
- Use code blocks to provide generic code examples. Whenever possible,
528
- use file blocks to provide the user with changes that apply to their bot.
529
-
530
- Link blocks:
531
- {{
532
- "type": "link",
533
- "text": "http://rasa.com/docs/rasa/core/flows"
534
- }}
535
-
536
- File blocks:
537
- {{
538
- "type": "file",
539
- "file": "flows.yml",
540
- "content": "- action: utter_greet\n next: END\n",
541
- }}
542
- Whenever possible, use file blocks to provide the user with changes
543
- that apply to their bot.
544
-
545
- You can use multiple content blocks to answer the user's question. Return
546
- as many content blocks as needed.
547
-
548
- Create links to https://rasa.com/docs for the user to take a look at.
549
-
550
- ----
551
- Logs from chatbot framework:
552
- {recent_logs_context}
553
-
554
- ----
555
- Most recent conversation of the user with the chatbot:
556
- {json.dumps(chat_bot_context)}
557
-
558
- ----
559
- Chat bot files for this project (name and content):
560
- {json.dumps(chat_bot_files_context)}
561
-
562
- ----
563
- Documentation Context:
564
- {formatted_results}
565
-
566
- ----
567
- Respond with a json array of content blocks. Keep the response
568
- short and concise.
569
- """)
570
-
571
- past_messages = []
572
- for message in user_chat_history:
573
- past_messages.append(
574
- {
575
- "role": "user" if message.get("type") == "user" else "assistant",
576
- "content": json.dumps(message.get("content")),
577
- }
578
- )
579
-
580
- messages = [
581
- {"role": "system", "content": prompt},
582
- *past_messages,
583
- ]
584
-
585
- llm_helper_schema = read_json_file(
586
- importlib_resources.files(PACKAGE_NAME).joinpath(
587
- "builder/llm-helper-schema.json"
588
- )
589
- )
590
-
591
- openai_response = await client.chat.completions.create(
592
- model="gpt-4.1-2025-04-14",
593
- messages=messages,
594
- response_format={
595
- "type": "json_schema",
596
- "json_schema": {
597
- "name": "llm_helper",
598
- "schema": llm_helper_schema,
599
- },
600
- },
601
- )
602
-
603
- return response.json(json.loads(openai_response.choices[0].message.content))
604
-
605
- @staticmethod
606
- def format_results(results):
607
- formatted_results = ""
608
- for result in results:
609
- formatted_result = f"<result url='{result.attributes.get('url')}'>"
610
- for part in result.content:
611
- formatted_result += f"<content>{part.text}</content>"
612
- formatted_results += formatted_result + "</result>"
613
- return f"<sources>{formatted_results}</sources>"
614
-
615
- async def train_and_load_agent(self):
616
- file_importer = self.importer_for_data()
617
- # this is used inside the training validation. validation assumes
618
- # that the endpoints are either stored in the default location or
619
- # that they have been loaded before - so that is what we do here.
620
- with tempfile.NamedTemporaryFile() as temp_file:
621
- temp_file.write(
622
- dump_obj_as_yaml_to_string(default_endpoints()).encode("utf-8")
623
- )
624
- temp_file.flush()
625
- AvailableEndpoints.reset_instance()
626
- read_endpoints_from_path(temp_file.name)
627
-
628
- available_endpoints = AvailableEndpoints.get_instance()
629
- assert available_endpoints is not None
630
-
631
- training_result = await train(
632
- domain="",
633
- config="",
634
- training_files=None,
635
- file_importer=file_importer,
636
- )
637
-
638
- structlogger.info(
639
- "prompt_to_bot.train_and_load_agent.training_result",
640
- training_result=training_result,
641
- )
642
-
643
- agent_instance = await agent.load_agent(
644
- model_path=training_result.model,
645
- remote_storage=None,
646
- endpoints=available_endpoints,
647
- loop=self.app.loop,
648
- )
649
-
650
- structlogger.info(
651
- "prompt_to_bot.train_and_load_agent.agent_instance",
652
- agent_instance=agent_instance,
653
- )
654
-
655
- if not agent_instance.is_ready():
656
- raise Exception(
657
- "Generation of the chatbot failed with an error (model failed "
658
- "to load). Please try again."
659
- )
660
-
661
- structlogger.info(
662
- "prompt_to_bot.train_and_load_agent.agent_ready",
663
- agent_instance=agent_instance,
664
- )
665
-
666
- self.input_channel.agent = agent_instance
667
- return agent_instance
668
-
669
-
670
- def main():
671
- """Start the Prompt to Bot service."""
672
- log_level = logging.DEBUG
673
- configure_logging_and_warnings(
674
- log_level=log_level,
675
- logging_config_file=None,
676
- warn_only_once=True,
677
- filter_repeated_logs=True,
678
- )
679
- configure_structlog(
680
- log_level,
681
- include_time=True,
682
- additional_processors=[
683
- collecting_logs_processor,
684
- ],
685
- )
686
-
687
- service = PromptToBotService()
688
- register_custom_sanic_error_handler(service.app)
689
-
690
- rasa.core.utils.list_routes(service.app)
691
-
692
- service.app.run(host="0.0.0.0", port=5005, legacy=True, motd=False)
693
-
694
-
695
- if __name__ == "__main__":
696
- main()