rasa-pro 3.14.0rc1__py3-none-any.whl → 3.14.0rc3__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 (84) hide show
  1. rasa/agents/protocol/a2a/a2a_agent.py +50 -42
  2. rasa/agents/utils.py +27 -5
  3. rasa/agents/validation.py +7 -9
  4. rasa/api.py +1 -2
  5. rasa/builder/copilot/constants.py +4 -1
  6. rasa/builder/copilot/copilot.py +191 -79
  7. rasa/builder/copilot/models.py +306 -116
  8. rasa/builder/copilot/prompts/copilot_system_prompt.jinja2 +33 -12
  9. rasa/builder/copilot/prompts/copilot_training_error_handler_prompt.jinja2 +53 -0
  10. rasa/builder/copilot/prompts/latest_user_message_context_prompt.jinja2 +59 -29
  11. rasa/builder/copilot/telemetry.py +8 -0
  12. rasa/builder/guardrails/policy_checker.py +1 -1
  13. rasa/builder/jobs.py +182 -12
  14. rasa/builder/models.py +12 -3
  15. rasa/builder/service.py +16 -2
  16. rasa/cli/dialogue_understanding_test.py +1 -0
  17. rasa/cli/e2e_test.py +1 -0
  18. rasa/cli/inspect.py +1 -0
  19. rasa/cli/project_templates/basic/tests/e2e_test_cases/without_stub/general/feedback.yml +46 -0
  20. rasa/cli/project_templates/basic/tests/e2e_test_cases/without_stub/general/goodbye.yml +9 -0
  21. rasa/cli/project_templates/basic/tests/e2e_test_cases/without_stub/general/help.yml +8 -0
  22. rasa/cli/project_templates/basic/tests/e2e_test_cases/without_stub/general/human_handoff.yml +41 -0
  23. rasa/cli/project_templates/basic/tests/e2e_test_cases/without_stub/general/patterns.yml +32 -0
  24. rasa/cli/project_templates/basic/tests/e2e_test_cases/without_stub/general/show_faqs.yml +8 -0
  25. rasa/cli/project_templates/finance/domain/general/help.yml +0 -0
  26. rasa/cli/project_templates/telco/data/network/flow_solve_internet_issue.yml +2 -2
  27. rasa/cli/project_templates/telco/domain/network/solve_internet_issue.yml +1 -2
  28. rasa/cli/project_templates/telco/tests/e2e_test_cases/with_stub/network/solve_internet_not_slow.yml +33 -0
  29. rasa/cli/project_templates/telco/tests/e2e_test_cases/with_stub/network/solve_internet_slow.yml +47 -0
  30. rasa/cli/project_templates/telco/tests/e2e_test_cases/without_stub/general/hello.yml +8 -0
  31. rasa/cli/run.py +1 -5
  32. rasa/cli/shell.py +1 -0
  33. rasa/cli/train.py +1 -0
  34. rasa/cli/validation/bot_config.py +7 -2
  35. rasa/core/available_agents.py +65 -55
  36. rasa/core/brokers/kafka.py +5 -1
  37. rasa/core/concurrent_lock_store.py +38 -21
  38. rasa/core/config/available_endpoints.py +0 -3
  39. rasa/core/config/configuration.py +36 -1
  40. rasa/core/constants.py +6 -0
  41. rasa/core/iam_credentials_providers/aws_iam_credentials_providers.py +69 -4
  42. rasa/core/iam_credentials_providers/credentials_provider_protocol.py +2 -1
  43. rasa/core/lock_store.py +4 -0
  44. rasa/core/policies/flows/agent_executor.py +16 -8
  45. rasa/core/redis_connection_factory.py +7 -2
  46. rasa/core/tracker_stores/redis_tracker_store.py +4 -0
  47. rasa/core/tracker_stores/sql_tracker_store.py +3 -1
  48. rasa/dialogue_understanding/commands/start_flow_command.py +10 -3
  49. rasa/dialogue_understanding/commands/utils.py +15 -4
  50. rasa/dialogue_understanding/generator/llm_based_command_generator.py +4 -2
  51. rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +4 -4
  52. rasa/dialogue_understanding/generator/single_step/search_ready_llm_command_generator.py +4 -4
  53. rasa/dialogue_understanding/generator/single_step/single_step_based_llm_command_generator.py +2 -2
  54. rasa/dialogue_understanding_test/du_test_runner.py +2 -2
  55. rasa/e2e_test/e2e_test_runner.py +2 -2
  56. rasa/shared/agents/auth/auth_strategy/oauth2_auth_strategy.py +10 -4
  57. rasa/shared/agents/auth/constants.py +1 -0
  58. rasa/shared/core/flows/steps/call.py +2 -2
  59. rasa/telemetry.py +3 -3
  60. rasa/validator.py +37 -0
  61. rasa/version.py +1 -1
  62. {rasa_pro-3.14.0rc1.dist-info → rasa_pro-3.14.0rc3.dist-info}/METADATA +14 -2
  63. {rasa_pro-3.14.0rc1.dist-info → rasa_pro-3.14.0rc3.dist-info}/RECORD +83 -73
  64. rasa/cli/project_templates/telco/tests/e2e_test_cases/network/solve_internet_issue.yml +0 -57
  65. /rasa/cli/project_templates/{finance/tests/e2e_test_cases → basic/tests/e2e_test_cases/without_stub}/general/hello.yml +0 -0
  66. /rasa/cli/project_templates/finance/tests/e2e_test_cases/{accounts → without_stub/accounts}/check_balance.yml +0 -0
  67. /rasa/cli/project_templates/finance/tests/e2e_test_cases/{accounts → without_stub/accounts}/download_statements.yml +0 -0
  68. /rasa/cli/project_templates/finance/tests/e2e_test_cases/{cards → without_stub/cards}/block_card.yml +0 -0
  69. /rasa/cli/project_templates/finance/tests/e2e_test_cases/{general → without_stub/general}/bot_challenge.yml +0 -0
  70. /rasa/cli/project_templates/finance/tests/e2e_test_cases/{general → without_stub/general}/feedback.yml +0 -0
  71. /rasa/cli/project_templates/finance/tests/e2e_test_cases/{general → without_stub/general}/goodbye.yml +0 -0
  72. /rasa/cli/project_templates/{telco/tests/e2e_test_cases → finance/tests/e2e_test_cases/without_stub}/general/hello.yml +0 -0
  73. /rasa/cli/project_templates/finance/tests/e2e_test_cases/{general → without_stub/general}/human_handoff.yml +0 -0
  74. /rasa/cli/project_templates/finance/tests/e2e_test_cases/{general → without_stub/general}/patterns.yml +0 -0
  75. /rasa/cli/project_templates/finance/tests/e2e_test_cases/{transfers → without_stub/transfers}/transfer_money.yml +0 -0
  76. /rasa/cli/project_templates/telco/tests/e2e_test_cases/{billing → without_stub/billing}/understand_bill.yml +0 -0
  77. /rasa/cli/project_templates/telco/tests/e2e_test_cases/{general → without_stub/general}/bot_challenge.yml +0 -0
  78. /rasa/cli/project_templates/telco/tests/e2e_test_cases/{general → without_stub/general}/feedback.yml +0 -0
  79. /rasa/cli/project_templates/telco/tests/e2e_test_cases/{general → without_stub/general}/goodbye.yml +0 -0
  80. /rasa/cli/project_templates/telco/tests/e2e_test_cases/{general → without_stub/general}/human_handoff.yml +0 -0
  81. /rasa/cli/project_templates/telco/tests/e2e_test_cases/{general → without_stub/general}/patterns.yml +0 -0
  82. {rasa_pro-3.14.0rc1.dist-info → rasa_pro-3.14.0rc3.dist-info}/NOTICE +0 -0
  83. {rasa_pro-3.14.0rc1.dist-info → rasa_pro-3.14.0rc3.dist-info}/WHEEL +0 -0
  84. {rasa_pro-3.14.0rc1.dist-info → rasa_pro-3.14.0rc3.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,53 @@
1
+ # Task
2
+ Your task is to help the user fix a training error that occurred when they clicked the
3
+ "Apply Changes" button. The user made changes to their assistant project files and tried
4
+ to apply them, but the training process failed. You must always explain the error within
5
+ the context of the user's **assistant project files** (flows, domain, actions, config).
6
+
7
+ ---
8
+
9
+ # Response guidelines
10
+
11
+ 1. **Summary** - Start with one short sentence that explains what happened in plain language.
12
+ 2. **Explanation** - Describe why this error occurs in Rasa Assistant, focusing only on
13
+ assistant project files. Be precise but simple. Do not speculate or reference Rasa
14
+ internals unrelated to project files.
15
+ 3. **Fix Steps** - Provide clear, numbered instructions.
16
+ - Point to specific files (`flows.yml`, `domain.yml`, `actions.py`, etc.).
17
+ - Include corrected YAML or Python snippets if needed.
18
+ - Validate syntax and indentation.
19
+ - Reference documentation for configuration options, syntax rules, or best practices.
20
+ 4. **Validation** - End with one line suggesting how to re-test
21
+ 5. **Multiple Errors** - If multiple error logs are present, you **must** address and
22
+ propose fixes for **all of them**, not just one.
23
+
24
+ # Context
25
+
26
+ ## Relevant Documentation
27
+
28
+ {% if documentation_results %}
29
+ The following documentation sources are available for reference. Use the source index
30
+ numbers (1, 2, 3, etc.) for inline citations when explaining Rasa concepts or providing
31
+ authoritative fixes:
32
+ ```
33
+ {{ documentation_results }}
34
+ ```
35
+ {% else %}
36
+ No relevant documentation source found.
37
+ {% endif %}
38
+
39
+ ## Modified assistant project files
40
+
41
+ {% if modified_files %}
42
+ {{ modified_files }}
43
+ {% else %}
44
+ No modified assistant project files.
45
+ {% endif %}
46
+
47
+ ## Available assistant logs
48
+
49
+ {% if logs %}
50
+ {{ logs }}
51
+ {% else %}
52
+ No assistant logs available.
53
+ {% endif %}
@@ -1,30 +1,33 @@
1
- # Context Available to You
2
- Treat everything **below** as **current-turn context** to apply under those rules. You
3
- have access to:
1
+ # Context Priority
2
+ When interpreting user questions (especially vague ones like *"What's this?"*), follow
3
+ this order of priority:
4
4
 
5
- {% if current_conversation %}
6
- ## Current Conversation and Conversation State between the user and the assistant
5
+ | Priority | Context Source | Usage |
6
+ |----------|----------------|-------|
7
+ | 1 | **Attachments** | Highest priority. If attachments exist, assume the user's question refers to them. Always ground explanations in attachment content first. |
8
+ | 2 | **Assistant State / Current Conversation** | Use this when attachments are not present. Supplement attachment knowledge with state info if needed. |
9
+ | 3 | **Assistant Files** | Use when neither attachments nor state provide enough context. |
10
+ | 4 | **Documentation Results** | Use to explain Rasa features and validate facts. Must always be cited inline. |
11
+ | 5 | **Assistant Logs** | Lowest priority. Use only if directly relevant to the user’s issue. |
7
12
 
8
- **Conversation History:**
9
- ```json
10
- {{ current_conversation }}
11
- ```
13
+ ---
12
14
 
13
- **Assistant's State:**
14
- ```json
15
- {{ current_state }}
16
- ```
17
- {% endif %}
15
+ # Remember!
16
+ - Focus on accessibility and efficiency. Give guidance users can act on right away.
17
+ - Keep answers concise, cut any fluff.
18
+ - Never impersonate or role-play as the assistant being built. You are the **Rasa assistant development expert**.
19
+ - Cite documentation inline frequently — every factual statement about Rasa features, concepts, or capabilities MUST be cited.
20
+ - Only cite from current turn documentation — never from previous conversation turns.
21
+ - NEVER add a separate list of URLs or sources — only use inline citations.
22
+ - NEVER start your response with a ``` or """ or any other quoting characters.
18
23
 
19
- {% if assistant_logs %}
20
- ***
24
+ ---
21
25
 
22
- ## Assistant Logs
23
- ```
24
- {{ assistant_logs }}
25
- ```
26
- {% endif %}
26
+ # Context Available to You
27
+ Treat everything **below** as context for the user's current request.
28
+ You have access to:
27
29
 
30
+ ***
28
31
 
29
32
  ## Assistant Files (Configuration, Domain, Flows)
30
33
  {% if assistant_files %}
@@ -51,11 +54,38 @@ numbers (1, 2, 3, etc.) for inline citations:
51
54
  No relevant documentation source found.
52
55
  {% endif %}
53
56
 
54
- # Remember!
55
- - Focus on accessibility and efficiency. Give guidance users can act on right away.
56
- - Keep answers concise, cut any fluff.
57
- - Never impersonate or role-play as the assistant being built. You are the **Rasa assistant development expert**.
58
- - Cite documentation inline frequently - every factual statement about Rasa features, concepts, or capabilities MUST be cited.
59
- - Only cite from current turn documentation - never from previous conversation turns
60
- - NEVER add a separate list of URLs or sources - only use inline citations.
61
- - NEVER start your response with a ``` or """ or any other quoting characters.
57
+ {% if current_conversation %}
58
+ ***
59
+
60
+ ## Current Conversation and Conversation State between the user and the assistant
61
+
62
+ **Conversation History:**
63
+ ```json
64
+ {{ current_conversation }}
65
+ ```
66
+
67
+ **Assistant's State:**
68
+ ```json
69
+ {{ current_state }}
70
+ ```
71
+ {% endif %}
72
+
73
+ {% if assistant_logs %}
74
+ ***
75
+
76
+ ## Assistant Logs
77
+ ```
78
+ {{ assistant_logs }}
79
+ ```
80
+ {% endif %}
81
+
82
+ {% if attachments %}
83
+ ***
84
+
85
+ ## Attachments
86
+ The user clicked **Ask Copilot** in the **Inspect Mode** and included additional context
87
+ as attachments:
88
+ ```json
89
+ {{ attachments }}
90
+ ```
91
+ {% endif %}
@@ -8,6 +8,7 @@ import structlog
8
8
  from rasa import telemetry
9
9
  from rasa.builder.copilot.constants import COPILOT_SEGMENT_WRITE_KEY_ENV_VAR
10
10
  from rasa.builder.copilot.copilot_response_handler import CopilotResponseHandler
11
+ from rasa.builder.copilot.models import EventContent
11
12
  from rasa.builder.document_retrieval.models import Document
12
13
  from rasa.telemetry import (
13
14
  SEGMENT_TRACK_ENDPOINT,
@@ -100,6 +101,7 @@ class CopilotTelemetry:
100
101
  system_message: Optional[dict[str, Any]] = None,
101
102
  chat_history: Optional[list[dict[str, Any]]] = None,
102
103
  last_user_message: Optional[str] = None,
104
+ tracker_event_attachments: Optional[list[dict[str, Any]]] = None,
103
105
  ) -> None:
104
106
  """Track a copilot message in the conversation.
105
107
 
@@ -115,6 +117,7 @@ class CopilotTelemetry:
115
117
  system_message: The system message used (optional).
116
118
  chat_history: The chat history messages used (optional).
117
119
  last_user_message: The last user message used (optional).
120
+ tracker_event_attachments: The tracker event attachments used (optional).
118
121
  """
119
122
  structlogger.debug("builder.telemetry.log_copilot_turn", text=text)
120
123
 
@@ -136,6 +139,7 @@ class CopilotTelemetry:
136
139
  "total_tokens": total_tokens,
137
140
  "chat_history": chat_history,
138
141
  "last_user_message": last_user_message,
142
+ "tracker_event_attachments": tracker_event_attachments,
139
143
  "timestamp": dt.datetime.utcnow().isoformat(),
140
144
  }
141
145
 
@@ -194,6 +198,7 @@ class CopilotTelemetry:
194
198
  system_message: dict[str, Any],
195
199
  chat_history: list[dict[str, Any]],
196
200
  last_user_message: Optional[str],
201
+ tracker_event_attachments: list[EventContent],
197
202
  ) -> None:
198
203
  """Log a copilot message from the response handler.
199
204
 
@@ -223,4 +228,7 @@ class CopilotTelemetry:
223
228
  system_message=system_message,
224
229
  chat_history=chat_history,
225
230
  last_user_message=last_user_message,
231
+ tracker_event_attachments=[
232
+ attachment.model_dump() for attachment in tracker_event_attachments
233
+ ],
226
234
  )
@@ -315,7 +315,7 @@ class GuardrailsPolicyChecker:
315
315
  continue
316
316
  if message.role != ROLE_USER:
317
317
  continue
318
- formatted_message = message.to_openai_format()
318
+ formatted_message = message.build_openai_message()
319
319
  text = (formatted_message.get("content") or "").strip()
320
320
  if not text:
321
321
  continue
rasa/builder/jobs.py CHANGED
@@ -1,9 +1,17 @@
1
- from typing import Any, Optional
1
+ from typing import Any, Dict, Optional
2
2
 
3
3
  import structlog
4
4
  from sanic import Sanic
5
5
 
6
6
  from rasa.builder import config
7
+ from rasa.builder.copilot.models import (
8
+ CopilotContext,
9
+ FileContent,
10
+ InternalCopilotRequestChatMessage,
11
+ LogContent,
12
+ ResponseCategory,
13
+ TrainingErrorLog,
14
+ )
7
15
  from rasa.builder.exceptions import (
8
16
  LLMGenerationError,
9
17
  ProjectGenerationError,
@@ -11,6 +19,7 @@ from rasa.builder.exceptions import (
11
19
  ValidationError,
12
20
  )
13
21
  from rasa.builder.job_manager import JobInfo, job_manager
22
+ from rasa.builder.llm_service import llm_service
14
23
  from rasa.builder.models import (
15
24
  JobStatus,
16
25
  JobStatusEvent,
@@ -28,9 +37,14 @@ structlogger = structlog.get_logger()
28
37
 
29
38
 
30
39
  async def push_job_status_event(
31
- job: JobInfo, status: JobStatus, message: Optional[str] = None
40
+ job: JobInfo,
41
+ status: JobStatus,
42
+ message: Optional[str] = None,
43
+ payload: Optional[Dict[str, Any]] = None,
32
44
  ) -> None:
33
- event = JobStatusEvent.from_status(status=status.value, message=message)
45
+ event = JobStatusEvent.from_status(
46
+ status=status.value, message=message, payload=payload
47
+ )
34
48
  job.status = status.value
35
49
  await job.put(event)
36
50
 
@@ -210,7 +224,7 @@ async def run_template_to_bot_job(
210
224
  async def run_replace_all_files_job(
211
225
  app: "Sanic",
212
226
  job: JobInfo,
213
- bot_files: dict,
227
+ bot_files: Dict[str, Any],
214
228
  ) -> None:
215
229
  """Run the replace-all-files job in the background.
216
230
 
@@ -228,7 +242,7 @@ async def run_replace_all_files_job(
228
242
  try:
229
243
  project_generator.replace_all_bot_files(bot_files)
230
244
 
231
- # 1. Validating
245
+ # Validating
232
246
  await push_job_status_event(job, JobStatus.validating)
233
247
  training_input = project_generator.get_training_input()
234
248
  validation_error = await validate_project(training_input.importer)
@@ -236,12 +250,13 @@ async def run_replace_all_files_job(
236
250
  raise ValidationError(validation_error)
237
251
  await push_job_status_event(job, JobStatus.validation_success)
238
252
 
239
- # 2. Training
253
+ # Training
240
254
  await push_job_status_event(job, JobStatus.training)
241
255
  agent = await train_and_load_agent(training_input)
242
256
  update_agent(agent, app)
243
257
  await push_job_status_event(job, JobStatus.train_success)
244
258
 
259
+ # Send final done event
245
260
  await push_job_status_event(job, JobStatus.done)
246
261
  job_manager.mark_done(job)
247
262
 
@@ -257,26 +272,181 @@ async def run_replace_all_files_job(
257
272
  included_log_levels=log_levels,
258
273
  )
259
274
  error_message = exc.get_error_message_with_logs(log_levels=log_levels)
260
- await push_job_status_event(
261
- job, JobStatus.validation_error, message=error_message
275
+ # Push error event and start copilot analysis job
276
+ await push_error_and_start_copilot_analysis(
277
+ app,
278
+ job,
279
+ JobStatus.validation_error,
280
+ error_message,
281
+ bot_files,
262
282
  )
283
+
284
+ # After error mark job as done
263
285
  job_manager.mark_done(job, error=error_message)
264
286
 
265
287
  except TrainingError as exc:
288
+ error_message = str(exc)
266
289
  structlogger.debug(
267
290
  "replace_all_files_job.train_error",
268
291
  job_id=job.id,
269
- error=str(exc),
292
+ error=error_message,
270
293
  )
271
- await push_job_status_event(job, JobStatus.train_error, message=str(exc))
272
- job_manager.mark_done(job, error=str(exc))
294
+ # Push error event and start copilot analysis job
295
+ await push_error_and_start_copilot_analysis(
296
+ app,
297
+ job,
298
+ JobStatus.train_error,
299
+ error_message,
300
+ bot_files,
301
+ )
302
+
303
+ # After error mark job as done
304
+ job_manager.mark_done(job, error=error_message)
273
305
 
274
306
  except Exception as exc:
275
307
  # Capture full traceback for anything truly unexpected
308
+ error_message = str(exc)
276
309
  structlogger.exception(
277
310
  "replace_all_files_job.unexpected_error",
278
311
  job_id=job.id,
312
+ error=error_message,
313
+ )
314
+
315
+ # Push error event and start copilot analysis job
316
+ await push_error_and_start_copilot_analysis(
317
+ app,
318
+ job,
319
+ JobStatus.error,
320
+ error_message,
321
+ bot_files,
322
+ )
323
+
324
+ # After error mark job as done
325
+ job_manager.mark_done(job, error=str(exc))
326
+
327
+
328
+ async def push_error_and_start_copilot_analysis(
329
+ app: "Sanic",
330
+ original_job: JobInfo,
331
+ original_job_status: JobStatus,
332
+ error_message: str,
333
+ bot_files: Dict[str, Any],
334
+ ) -> None:
335
+ """Start a copilot analysis job and notify the client.
336
+
337
+ Creates a copilot analysis job and sends the new job ID to the client. The new
338
+ job runs in the background.
339
+
340
+ Args:
341
+ app: The Sanic application instance
342
+ original_job: The original job that failed
343
+ original_job_status: The status of the job that failed
344
+ error_message: The error message to analyze
345
+ bot_files: The bot files to include in analysis
346
+ """
347
+ # Create a copilot analysis job. Send the new job ID to the client and
348
+ # run the Copilot Analysis job in the background.
349
+ message = "Failed to train the assistant. Starting copilot analysis."
350
+
351
+ copilot_job = job_manager.create_job()
352
+ # Push the error status event for the original job
353
+ await push_job_status_event(
354
+ original_job,
355
+ original_job_status,
356
+ message=message,
357
+ payload={"copilot_job_id": copilot_job.id},
358
+ )
359
+ # Run the copilot analysis job in the background
360
+ app.add_task(
361
+ run_copilot_training_error_analysis_job(
362
+ app, copilot_job, error_message, bot_files
363
+ )
364
+ )
365
+ structlogger.debug(
366
+ f"update_files_job.{original_job_status.value}.copilot_analysis_start",
367
+ event_info=message,
368
+ job_id=original_job.id,
369
+ error=error_message,
370
+ copilot_job_id=copilot_job.id,
371
+ )
372
+
373
+
374
+ async def run_copilot_training_error_analysis_job(
375
+ app: "Sanic",
376
+ job: JobInfo,
377
+ training_error_message: str,
378
+ bot_files: Dict[str, Any],
379
+ ) -> None:
380
+ """Run copilot training error analysis job."""
381
+ await push_job_status_event(job, JobStatus.received)
382
+
383
+ try:
384
+ # Create message content blocks with log content and available files
385
+ log_content_block = LogContent(
386
+ type="log", content=training_error_message, context="training_error"
387
+ )
388
+ file_content_blocks = [
389
+ FileContent(type="file", file_path=file_path, file_content=file_content)
390
+ for file_path, file_content in bot_files.items()
391
+ ]
392
+ context = CopilotContext(
393
+ tracker_context=None, # No conversation context needed
394
+ copilot_chat_history=[
395
+ InternalCopilotRequestChatMessage(
396
+ role="internal_copilot_request",
397
+ content=[log_content_block, *file_content_blocks],
398
+ response_category=ResponseCategory.TRAINING_ERROR_LOG_ANALYSIS,
399
+ )
400
+ ],
401
+ )
402
+
403
+ # Generate copilot response
404
+ copilot_client = llm_service.instantiate_copilot()
405
+ (
406
+ original_stream,
407
+ generation_context,
408
+ ) = await copilot_client.generate_response(context)
409
+
410
+ copilot_response_handler = llm_service.instantiate_handler(
411
+ config.COPILOT_HANDLER_ROLLING_BUFFER_SIZE
412
+ )
413
+ intercepted_stream = copilot_response_handler.handle_response(original_stream)
414
+
415
+ # Stream the copilot response as job events
416
+ async for token in intercepted_stream:
417
+ # Send each token as a job event using the same format as /copilot endpoint
418
+ await push_job_status_event(
419
+ job, JobStatus.copilot_analyzing, payload=token.sse_data
420
+ )
421
+
422
+ # Send references (if any) as part of copilot_analyzing stream
423
+ if generation_context.relevant_documents:
424
+ reference_section = copilot_response_handler.extract_references(
425
+ generation_context.relevant_documents
426
+ )
427
+ await push_job_status_event(
428
+ job, JobStatus.copilot_analyzing, payload=reference_section.sse_data
429
+ )
430
+
431
+ # Send original error log as part of copilot_analyzing stream
432
+ training_error_log = TrainingErrorLog(logs=[log_content_block])
433
+ await push_job_status_event(
434
+ job, JobStatus.copilot_analyzing, payload=training_error_log.sse_data
435
+ )
436
+
437
+ # Send success status
438
+ await push_job_status_event(job, JobStatus.copilot_analysis_success)
439
+
440
+ await push_job_status_event(job, JobStatus.done)
441
+ job_manager.mark_done(job)
442
+
443
+ except Exception as exc:
444
+ structlogger.exception(
445
+ "copilot_training_error_analysis_job.error",
446
+ job_id=job.id,
279
447
  error=str(exc),
280
448
  )
281
- await push_job_status_event(job, JobStatus.error, message=str(exc))
449
+ await push_job_status_event(
450
+ job, JobStatus.copilot_analysis_error, message=str(exc)
451
+ )
282
452
  job_manager.mark_done(job, error=str(exc))
rasa/builder/models.py CHANGED
@@ -136,12 +136,14 @@ class JobStatusEvent(ServerSentEvent):
136
136
  cls,
137
137
  status: str,
138
138
  message: Optional[str] = None,
139
+ payload: Optional[Dict[str, Any]] = None,
139
140
  ) -> "JobStatusEvent":
140
141
  """Factory for job-status events.
141
142
 
142
143
  Args:
143
144
  status: The job status (e.g. "training", "train_success").
144
145
  message: Optional error message for error events.
146
+ payload: Optional additional payload data to include in the event.
145
147
 
146
148
  Returns:
147
149
  A JobStatusEvent instance with the appropriate event type and data.
@@ -150,11 +152,13 @@ class JobStatusEvent(ServerSentEvent):
150
152
  ServerSentEventType.error if message else ServerSentEventType.progress
151
153
  )
152
154
 
153
- payload: Dict[str, Any] = {"status": status}
155
+ event_payload: Dict[str, Any] = {"status": status}
154
156
  if message:
155
- payload["message"] = message
157
+ event_payload["message"] = message
158
+ if payload:
159
+ event_payload.update(payload)
156
160
 
157
- return cls(event=event_type.value, data=payload)
161
+ return cls(event=event_type.value, data=event_payload)
158
162
 
159
163
 
160
164
  class ValidationResult(BaseModel):
@@ -193,6 +197,11 @@ class JobStatus(str, Enum):
193
197
  validation_success = "validation_success"
194
198
  validation_error = "validation_error"
195
199
 
200
+ copilot_analysis_start = "copilot_analysis_start"
201
+ copilot_analyzing = "copilot_analyzing"
202
+ copilot_analysis_success = "copilot_analysis_success"
203
+ copilot_analysis_error = "copilot_analysis_error"
204
+
196
205
 
197
206
  class JobCreateResponse(BaseModel):
198
207
  job_id: str = Field(...)
rasa/builder/service.py CHANGED
@@ -532,7 +532,19 @@ async def get_bot_files(request: Request) -> HTTPResponse:
532
532
  "**Error Events (can occur at any time):**\n"
533
533
  "- `validation_error` - Bot configuration files are invalid\n"
534
534
  "- `train_error` - Files updated but training failed\n"
535
+ "- `copilot_analysis_start` - Copilot analysis started (includes `copilot_job_id` "
536
+ "in payload)\n"
535
537
  "- `error` - Unexpected error occurred\n\n"
538
+ "**Copilot Analysis Events:**\n"
539
+ "When training or validation fails, a separate copilot analysis job is "
540
+ "automatically started. The `copilot_analysis_start` event includes a "
541
+ "`copilot_job_id` in the payload.\nConnect to `/job-events/<copilot_job_id>` to "
542
+ "receive the following events:\n"
543
+ "- `copilot_analyzing` - Copilot is analyzing errors and providing suggestions. "
544
+ "Uses the same SSE event payload format as the `/copilot` endpoint with `content`, "
545
+ "`response_category`, and `completeness` fields.\n"
546
+ "- `copilot_analysis_success` - Copilot analysis completed with references.\n"
547
+ "- `copilot_analysis_error` - Copilot analysis failed\n\n"
536
548
  "**Usage:**\n"
537
549
  "1. Send POST request with Content-Type: application/json\n"
538
550
  "2. The response will be a JSON object `{job_id: ...}`\n"
@@ -1042,7 +1054,8 @@ async def copilot(request: Request) -> None:
1042
1054
  # Offload telemetry logging to a background task
1043
1055
  request.app.add_task(
1044
1056
  asyncio.to_thread(
1045
- telemetry.log_user_turn, req.last_message.get_text_content()
1057
+ telemetry.log_user_turn,
1058
+ req.last_message.get_flattened_text_content(),
1046
1059
  )
1047
1060
  )
1048
1061
 
@@ -1159,10 +1172,11 @@ async def copilot(request: Request) -> None:
1159
1172
  system_message=generation_context.system_message,
1160
1173
  chat_history=generation_context.chat_history,
1161
1174
  last_user_message=(
1162
- req.last_message.get_text_content()
1175
+ req.last_message.get_flattened_text_content()
1163
1176
  if (req.last_message and req.last_message.role == ROLE_USER)
1164
1177
  else None
1165
1178
  ),
1179
+ tracker_event_attachments=generation_context.tracker_event_attachments,
1166
1180
  **copilot_client.usage_statistics.model_dump(),
1167
1181
  )
1168
1182
  )
@@ -214,6 +214,7 @@ def execute_dialogue_understanding_tests(args: argparse.Namespace) -> None:
214
214
 
215
215
  # initialization of endpoints
216
216
  endpoints = set_up_available_endpoints(args)
217
+ Configuration.initialise_sub_agents(args.sub_agents)
217
218
 
218
219
  # set up the test runner, e.g. start the agent
219
220
  try:
rasa/cli/e2e_test.py CHANGED
@@ -160,6 +160,7 @@ def execute_e2e_tests(args: argparse.Namespace) -> None:
160
160
  endpoints = Configuration.initialise_endpoints(
161
161
  endpoints_path=EndpointsConfigPath.validate(args.endpoints)
162
162
  ).endpoints
163
+ Configuration.initialise_sub_agents(args.sub_agents)
163
164
 
164
165
  # Ignore all endpoints apart from action server, model, nlu and nlg
165
166
  # to ensure InMemoryTrackerStore is being used instead of production
rasa/cli/inspect.py CHANGED
@@ -93,6 +93,7 @@ def inspect(args: argparse.Namespace) -> None:
93
93
  # it can be used safely throughout the codebase with
94
94
  # `Configuration.get_instance().endpoints`
95
95
  Configuration.initialise_endpoints(endpoints_path=Path(args.endpoints))
96
+ Configuration.initialise_sub_agents(args.sub_agents)
96
97
 
97
98
  try:
98
99
  model = get_local_model(model)
@@ -0,0 +1,46 @@
1
+ test_cases:
2
+ - test_case: positive feedback
3
+ steps:
4
+ - user: "goodbye"
5
+ assertions:
6
+ - flow_started: "goodbye_flow"
7
+ - bot_uttered:
8
+ utter_name: "utter_goodbye"
9
+ - flow_started: "leave_feedback"
10
+ - bot_uttered:
11
+ utter_name: "utter_ask_feedback_rating"
12
+ buttons:
13
+ - title: "👍 Great"
14
+ payload: "/SetSlots(feedback_rating=thumbs_up)"
15
+ - title: "👎 Could be better"
16
+ payload: "/SetSlots(feedback_rating=thumbs_down)"
17
+ - user: "/SetSlots(feedback_rating=thumbs_up)"
18
+ assertions:
19
+ - slot_was_set:
20
+ - name: "feedback_rating"
21
+ value: "thumbs_up"
22
+ - bot_uttered:
23
+ utter_name: "utter_thankyou_positive"
24
+ #
25
+ - test_case: negative feedback
26
+ steps:
27
+ - user: "goodbye"
28
+ assertions:
29
+ - flow_started: "goodbye_flow"
30
+ - bot_uttered:
31
+ utter_name: "utter_goodbye"
32
+ - flow_started: "leave_feedback"
33
+ - bot_uttered:
34
+ utter_name: "utter_ask_feedback_rating"
35
+ buttons:
36
+ - title: "👍 Great"
37
+ payload: "/SetSlots(feedback_rating=thumbs_up)"
38
+ - title: "👎 Could be better"
39
+ payload: "/SetSlots(feedback_rating=thumbs_down)"
40
+ - user: "/SetSlots(feedback_rating=thumbs_down)"
41
+ assertions:
42
+ - slot_was_set:
43
+ - name: "feedback_rating"
44
+ value: "thumbs_down"
45
+ - bot_uttered:
46
+ utter_name: "utter_thankyou_negative"
@@ -0,0 +1,9 @@
1
+ test_cases:
2
+ - test_case: goodbye
3
+ steps:
4
+ - user: "goodbye"
5
+ assertions:
6
+ - flow_started: "goodbye_flow"
7
+ - bot_uttered:
8
+ utter_name: "utter_goodbye"
9
+ - flow_started: "leave_feedback"
@@ -0,0 +1,8 @@
1
+ test_cases:
2
+ - test_case: help
3
+ steps:
4
+ - user: "help"
5
+ assertions:
6
+ - flow_started: "help"
7
+ - bot_uttered:
8
+ utter_name: "utter_what_can_you_do"