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.
- rasa/agents/protocol/a2a/a2a_agent.py +50 -42
- rasa/agents/utils.py +27 -5
- rasa/agents/validation.py +7 -9
- rasa/api.py +1 -2
- rasa/builder/copilot/constants.py +4 -1
- rasa/builder/copilot/copilot.py +191 -79
- rasa/builder/copilot/models.py +306 -116
- rasa/builder/copilot/prompts/copilot_system_prompt.jinja2 +33 -12
- rasa/builder/copilot/prompts/copilot_training_error_handler_prompt.jinja2 +53 -0
- rasa/builder/copilot/prompts/latest_user_message_context_prompt.jinja2 +59 -29
- rasa/builder/copilot/telemetry.py +8 -0
- rasa/builder/guardrails/policy_checker.py +1 -1
- rasa/builder/jobs.py +182 -12
- rasa/builder/models.py +12 -3
- rasa/builder/service.py +16 -2
- rasa/cli/dialogue_understanding_test.py +1 -0
- rasa/cli/e2e_test.py +1 -0
- rasa/cli/inspect.py +1 -0
- rasa/cli/project_templates/basic/tests/e2e_test_cases/without_stub/general/feedback.yml +46 -0
- rasa/cli/project_templates/basic/tests/e2e_test_cases/without_stub/general/goodbye.yml +9 -0
- rasa/cli/project_templates/basic/tests/e2e_test_cases/without_stub/general/help.yml +8 -0
- rasa/cli/project_templates/basic/tests/e2e_test_cases/without_stub/general/human_handoff.yml +41 -0
- rasa/cli/project_templates/basic/tests/e2e_test_cases/without_stub/general/patterns.yml +32 -0
- rasa/cli/project_templates/basic/tests/e2e_test_cases/without_stub/general/show_faqs.yml +8 -0
- rasa/cli/project_templates/finance/domain/general/help.yml +0 -0
- rasa/cli/project_templates/telco/data/network/flow_solve_internet_issue.yml +2 -2
- rasa/cli/project_templates/telco/domain/network/solve_internet_issue.yml +1 -2
- rasa/cli/project_templates/telco/tests/e2e_test_cases/with_stub/network/solve_internet_not_slow.yml +33 -0
- rasa/cli/project_templates/telco/tests/e2e_test_cases/with_stub/network/solve_internet_slow.yml +47 -0
- rasa/cli/project_templates/telco/tests/e2e_test_cases/without_stub/general/hello.yml +8 -0
- rasa/cli/run.py +1 -5
- rasa/cli/shell.py +1 -0
- rasa/cli/train.py +1 -0
- rasa/cli/validation/bot_config.py +7 -2
- rasa/core/available_agents.py +65 -55
- rasa/core/brokers/kafka.py +5 -1
- rasa/core/concurrent_lock_store.py +38 -21
- rasa/core/config/available_endpoints.py +0 -3
- rasa/core/config/configuration.py +36 -1
- rasa/core/constants.py +6 -0
- rasa/core/iam_credentials_providers/aws_iam_credentials_providers.py +69 -4
- rasa/core/iam_credentials_providers/credentials_provider_protocol.py +2 -1
- rasa/core/lock_store.py +4 -0
- rasa/core/policies/flows/agent_executor.py +16 -8
- rasa/core/redis_connection_factory.py +7 -2
- rasa/core/tracker_stores/redis_tracker_store.py +4 -0
- rasa/core/tracker_stores/sql_tracker_store.py +3 -1
- rasa/dialogue_understanding/commands/start_flow_command.py +10 -3
- rasa/dialogue_understanding/commands/utils.py +15 -4
- rasa/dialogue_understanding/generator/llm_based_command_generator.py +4 -2
- rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +4 -4
- rasa/dialogue_understanding/generator/single_step/search_ready_llm_command_generator.py +4 -4
- rasa/dialogue_understanding/generator/single_step/single_step_based_llm_command_generator.py +2 -2
- rasa/dialogue_understanding_test/du_test_runner.py +2 -2
- rasa/e2e_test/e2e_test_runner.py +2 -2
- rasa/shared/agents/auth/auth_strategy/oauth2_auth_strategy.py +10 -4
- rasa/shared/agents/auth/constants.py +1 -0
- rasa/shared/core/flows/steps/call.py +2 -2
- rasa/telemetry.py +3 -3
- rasa/validator.py +37 -0
- rasa/version.py +1 -1
- {rasa_pro-3.14.0rc1.dist-info → rasa_pro-3.14.0rc3.dist-info}/METADATA +14 -2
- {rasa_pro-3.14.0rc1.dist-info → rasa_pro-3.14.0rc3.dist-info}/RECORD +83 -73
- rasa/cli/project_templates/telco/tests/e2e_test_cases/network/solve_internet_issue.yml +0 -57
- /rasa/cli/project_templates/{finance/tests/e2e_test_cases → basic/tests/e2e_test_cases/without_stub}/general/hello.yml +0 -0
- /rasa/cli/project_templates/finance/tests/e2e_test_cases/{accounts → without_stub/accounts}/check_balance.yml +0 -0
- /rasa/cli/project_templates/finance/tests/e2e_test_cases/{accounts → without_stub/accounts}/download_statements.yml +0 -0
- /rasa/cli/project_templates/finance/tests/e2e_test_cases/{cards → without_stub/cards}/block_card.yml +0 -0
- /rasa/cli/project_templates/finance/tests/e2e_test_cases/{general → without_stub/general}/bot_challenge.yml +0 -0
- /rasa/cli/project_templates/finance/tests/e2e_test_cases/{general → without_stub/general}/feedback.yml +0 -0
- /rasa/cli/project_templates/finance/tests/e2e_test_cases/{general → without_stub/general}/goodbye.yml +0 -0
- /rasa/cli/project_templates/{telco/tests/e2e_test_cases → finance/tests/e2e_test_cases/without_stub}/general/hello.yml +0 -0
- /rasa/cli/project_templates/finance/tests/e2e_test_cases/{general → without_stub/general}/human_handoff.yml +0 -0
- /rasa/cli/project_templates/finance/tests/e2e_test_cases/{general → without_stub/general}/patterns.yml +0 -0
- /rasa/cli/project_templates/finance/tests/e2e_test_cases/{transfers → without_stub/transfers}/transfer_money.yml +0 -0
- /rasa/cli/project_templates/telco/tests/e2e_test_cases/{billing → without_stub/billing}/understand_bill.yml +0 -0
- /rasa/cli/project_templates/telco/tests/e2e_test_cases/{general → without_stub/general}/bot_challenge.yml +0 -0
- /rasa/cli/project_templates/telco/tests/e2e_test_cases/{general → without_stub/general}/feedback.yml +0 -0
- /rasa/cli/project_templates/telco/tests/e2e_test_cases/{general → without_stub/general}/goodbye.yml +0 -0
- /rasa/cli/project_templates/telco/tests/e2e_test_cases/{general → without_stub/general}/human_handoff.yml +0 -0
- /rasa/cli/project_templates/telco/tests/e2e_test_cases/{general → without_stub/general}/patterns.yml +0 -0
- {rasa_pro-3.14.0rc1.dist-info → rasa_pro-3.14.0rc3.dist-info}/NOTICE +0 -0
- {rasa_pro-3.14.0rc1.dist-info → rasa_pro-3.14.0rc3.dist-info}/WHEEL +0 -0
- {rasa_pro-3.14.0rc1.dist-info → rasa_pro-3.14.0rc3.dist-info}/entry_points.txt +0 -0
rasa/builder/copilot/models.py
CHANGED
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
2
|
from enum import Enum
|
|
3
|
-
from typing import Any, Dict, List, Literal, Optional, Union
|
|
3
|
+
from typing import Any, Dict, List, Literal, Optional, Type, TypeVar, Union
|
|
4
4
|
|
|
5
5
|
import structlog
|
|
6
6
|
from openai.types.chat.chat_completion_chunk import ChatCompletionChunk
|
|
7
|
-
from pydantic import
|
|
7
|
+
from pydantic import (
|
|
8
|
+
BaseModel,
|
|
9
|
+
Field,
|
|
10
|
+
field_serializer,
|
|
11
|
+
field_validator,
|
|
12
|
+
model_validator,
|
|
13
|
+
)
|
|
8
14
|
from typing_extensions import Annotated
|
|
9
15
|
|
|
10
16
|
from rasa.builder.copilot.constants import (
|
|
11
17
|
ROLE_ASSISTANT,
|
|
12
18
|
ROLE_COPILOT,
|
|
13
19
|
ROLE_COPILOT_INTERNAL,
|
|
20
|
+
ROLE_SYSTEM,
|
|
14
21
|
ROLE_USER,
|
|
15
22
|
)
|
|
16
23
|
from rasa.builder.document_retrieval.models import Document
|
|
@@ -49,7 +56,8 @@ class ResponseCategory(Enum):
|
|
|
49
56
|
# When Copilot analyzes error logs and provides suggestions
|
|
50
57
|
TRAINING_ERROR_LOG_ANALYSIS = "training_error_log_analysis"
|
|
51
58
|
E2E_TESTING_ERROR_LOG_ANALYSIS = "e2e_testing_error_log_analysis"
|
|
52
|
-
|
|
59
|
+
TRAINING_ERROR_LOG = "training_error_log"
|
|
60
|
+
E2E_TESTING_ERROR_LOG = "e2e_testing_error_log"
|
|
53
61
|
# Conversation history signature
|
|
54
62
|
SIGNATURE = "signature"
|
|
55
63
|
|
|
@@ -90,7 +98,7 @@ class LogContent(BaseContent):
|
|
|
90
98
|
type: Literal["log"]
|
|
91
99
|
content: str = Field(..., description="Logs, error messages, stack traces, etc.")
|
|
92
100
|
context: Optional[str] = Field(
|
|
93
|
-
None,
|
|
101
|
+
default=None,
|
|
94
102
|
description=(
|
|
95
103
|
"Additional, optional context description for the logs "
|
|
96
104
|
"(e.g., 'training session', 'e2e testing run', 'deployment process')"
|
|
@@ -102,7 +110,7 @@ class LogContent(BaseContent):
|
|
|
102
110
|
)
|
|
103
111
|
|
|
104
112
|
|
|
105
|
-
class EventContent(
|
|
113
|
+
class EventContent(BaseContent):
|
|
106
114
|
type: Literal["event"]
|
|
107
115
|
event: str = Field(..., description="The event's type_name")
|
|
108
116
|
|
|
@@ -144,125 +152,220 @@ ContentBlock = Annotated[
|
|
|
144
152
|
),
|
|
145
153
|
]
|
|
146
154
|
|
|
155
|
+
TContentBlock = TypeVar("TContentBlock", bound=BaseContent)
|
|
147
156
|
|
|
148
|
-
class CopilotChatMessage(BaseModel):
|
|
149
|
-
"""Model for a single chat messages between the user and the copilot."""
|
|
150
157
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
158
|
+
class BaseCopilotChatMessage(BaseModel, ABC):
|
|
159
|
+
role: str
|
|
160
|
+
response_category: Optional[ResponseCategory] = Field(default=None)
|
|
161
|
+
|
|
162
|
+
@abstractmethod
|
|
163
|
+
def build_openai_message(self, *args, **kwargs) -> Dict[str, Any]: # type: ignore[no-untyped-def]
|
|
164
|
+
pass
|
|
165
|
+
|
|
166
|
+
@field_serializer("response_category", when_used="always")
|
|
167
|
+
def _serialize_response_category(
|
|
168
|
+
self, v: Optional[ResponseCategory]
|
|
169
|
+
) -> Optional[str]:
|
|
170
|
+
"""Serializing CopilotChatMessage, response_category should be a string."""
|
|
171
|
+
return None if v is None else v.value
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
class BaseContentBlockCopilotChatMessage(BaseCopilotChatMessage, ABC):
|
|
175
|
+
"""Base class for messages that contain ContentBlock lists."""
|
|
176
|
+
|
|
156
177
|
content: List[ContentBlock]
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
178
|
+
|
|
179
|
+
def get_flattened_text_content(self) -> str:
|
|
180
|
+
"""Get the text content from the message."""
|
|
181
|
+
return "\n".join(
|
|
182
|
+
content_block.text
|
|
183
|
+
for content_block in self.content
|
|
184
|
+
if isinstance(content_block, TextContent)
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
def get_flattened_log_content(self) -> str:
|
|
188
|
+
"""Get the log content from the message."""
|
|
189
|
+
return "\n".join(
|
|
190
|
+
content_block.content
|
|
191
|
+
for content_block in self.content
|
|
192
|
+
if isinstance(content_block, LogContent)
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
def get_content_blocks_by_type(
|
|
196
|
+
self, content_type: Type[TContentBlock]
|
|
197
|
+
) -> List[TContentBlock]:
|
|
198
|
+
"""Get the content blocks from the message by type."""
|
|
199
|
+
return [
|
|
200
|
+
content_block
|
|
201
|
+
for content_block in self.content
|
|
202
|
+
if isinstance(content_block, content_type)
|
|
203
|
+
]
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
class CopilotSystemMessage(BaseCopilotChatMessage):
|
|
207
|
+
role: Literal["system"] = Field(
|
|
208
|
+
default=ROLE_SYSTEM,
|
|
209
|
+
pattern=f"^{ROLE_SYSTEM}",
|
|
210
|
+
description="The system message that sets the system instructions for the LLM.",
|
|
164
211
|
)
|
|
165
212
|
|
|
213
|
+
def build_openai_message(self, prompt: str, *args, **kwargs) -> Dict[str, Any]: # type: ignore[no-untyped-def]
|
|
214
|
+
"""Render the system message template and return OpenAI format."""
|
|
215
|
+
return {"role": ROLE_SYSTEM, "content": prompt}
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
class UserChatMessage(BaseContentBlockCopilotChatMessage):
|
|
219
|
+
role: Literal["user"] = Field(
|
|
220
|
+
default=ROLE_USER,
|
|
221
|
+
pattern=f"^{ROLE_USER}",
|
|
222
|
+
description="The user who sent the message.",
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
@classmethod
|
|
226
|
+
@field_validator("content")
|
|
227
|
+
def must_have_at_least_one_text(cls, v: List[ContentBlock]) -> List[ContentBlock]:
|
|
228
|
+
if not any(isinstance(content_block, TextContent) for content_block in v):
|
|
229
|
+
message = "User role messages must have at least one `TextContent` block."
|
|
230
|
+
structlogger.error(
|
|
231
|
+
"user_chat_message.missing_text_content",
|
|
232
|
+
event_info=message,
|
|
233
|
+
content=v,
|
|
234
|
+
)
|
|
235
|
+
raise ValueError(
|
|
236
|
+
"UserChatMessage must contain at least one TextContent block."
|
|
237
|
+
)
|
|
238
|
+
return v
|
|
239
|
+
|
|
166
240
|
@model_validator(mode="after")
|
|
167
|
-
def
|
|
168
|
-
"""Validate value of response_category for
|
|
241
|
+
def validate_response_category(self) -> "UserChatMessage":
|
|
242
|
+
"""Validate value of response_category for user message.
|
|
169
243
|
|
|
170
244
|
For 'user' role messages, only None or GUARDRAILS_POLICY_VIOLATION are allowed.
|
|
171
|
-
For 'copilot' role messages, any category is permitted.
|
|
172
|
-
For 'rasa_internal' role messages, any category is permitted.
|
|
173
245
|
"""
|
|
246
|
+
allowed_response_categories = [ResponseCategory.GUARDRAILS_POLICY_VIOLATION]
|
|
174
247
|
if (
|
|
175
|
-
self.
|
|
176
|
-
and self.response_category
|
|
177
|
-
and self.response_category != ResponseCategory.GUARDRAILS_POLICY_VIOLATION
|
|
248
|
+
self.response_category is not None
|
|
249
|
+
and self.response_category not in allowed_response_categories
|
|
178
250
|
):
|
|
179
251
|
message = (
|
|
180
252
|
f"User role messages can only have response_category of `None` or "
|
|
181
|
-
f"
|
|
182
|
-
f"
|
|
253
|
+
f"{', '.join(category.value for category in allowed_response_categories)}." # noqa: E501
|
|
254
|
+
f"Got `{self.response_category}`."
|
|
183
255
|
)
|
|
184
256
|
structlogger.error(
|
|
185
|
-
"
|
|
257
|
+
"user_chat_message.validate_response_category"
|
|
186
258
|
".invalid_response_category",
|
|
187
259
|
event_info=message,
|
|
188
260
|
response_category=self.response_category,
|
|
261
|
+
allowed_response_categories=allowed_response_categories,
|
|
189
262
|
role=self.role,
|
|
190
263
|
)
|
|
191
264
|
raise ValueError(message)
|
|
192
265
|
|
|
193
266
|
return self
|
|
194
267
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
)
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
"""Concatenate all 'log' content blocks into a single string."""
|
|
212
|
-
return "\n".join(
|
|
213
|
-
content_block.content
|
|
214
|
-
for content_block in self.content
|
|
215
|
-
if isinstance(content_block, LogContent)
|
|
216
|
-
)
|
|
268
|
+
def build_openai_message( # type: ignore[no-untyped-def]
|
|
269
|
+
self, prompt: Optional[str] = None, *args, **kwargs
|
|
270
|
+
) -> Dict[str, Any]:
|
|
271
|
+
# If a prompt is provided, add it to the message content as additional
|
|
272
|
+
# instructions
|
|
273
|
+
if prompt:
|
|
274
|
+
return {
|
|
275
|
+
"role": ROLE_USER,
|
|
276
|
+
"content": [
|
|
277
|
+
{"type": "text", "text": prompt},
|
|
278
|
+
{"type": "text", "text": self.get_flattened_text_content()},
|
|
279
|
+
],
|
|
280
|
+
}
|
|
281
|
+
# Return simple text content (useful for showing the history)
|
|
282
|
+
else:
|
|
283
|
+
return {"role": ROLE_USER, "content": self.get_flattened_text_content()}
|
|
217
284
|
|
|
218
|
-
def to_openai_format(self) -> Dict[str, Any]:
|
|
219
|
-
"""Convert to OpenAI message format for API calls."""
|
|
220
|
-
role_to_openai_format = {
|
|
221
|
-
ROLE_USER: self._user_message_to_openai_format,
|
|
222
|
-
ROLE_COPILOT: self._copilot_message_to_openai_format,
|
|
223
|
-
ROLE_COPILOT_INTERNAL: self._copilot_message_to_openai_format,
|
|
224
|
-
}
|
|
225
|
-
return role_to_openai_format[self.role]()
|
|
226
285
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
content = self.get_text_content()
|
|
230
|
-
return {"role": role, "content": content}
|
|
286
|
+
class CopilotChatMessage(BaseContentBlockCopilotChatMessage):
|
|
287
|
+
role: Literal["copilot"]
|
|
231
288
|
|
|
232
|
-
def
|
|
233
|
-
role = self._map_role_to_openai()
|
|
289
|
+
def build_openai_message(self, *args, **kwargs) -> Dict[str, Any]: # type: ignore[no-untyped-def]
|
|
234
290
|
# For now the Copilot responds only with the text content and all the content
|
|
235
291
|
# is formatted as a markdown.
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
292
|
+
return {"role": ROLE_ASSISTANT, "content": self.get_flattened_text_content()}
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
class InternalCopilotRequestChatMessage(BaseContentBlockCopilotChatMessage):
|
|
296
|
+
role: Literal["internal_copilot_request"]
|
|
297
|
+
|
|
298
|
+
@model_validator(mode="after")
|
|
299
|
+
def validate_response_category(self) -> "InternalCopilotRequestChatMessage":
|
|
300
|
+
"""Validate value of response_category for internal copilot request message.
|
|
301
|
+
|
|
302
|
+
For 'internal_copilot_request' role messages, only `TRAINING_ERROR_LOG_ANALYSIS`
|
|
303
|
+
and `E2E_TESTING_ERROR_LOG_ANALYSIS` response categories are allowed.
|
|
304
|
+
"""
|
|
305
|
+
allowed_response_categories = [
|
|
306
|
+
ResponseCategory.TRAINING_ERROR_LOG_ANALYSIS,
|
|
307
|
+
ResponseCategory.E2E_TESTING_ERROR_LOG_ANALYSIS,
|
|
308
|
+
]
|
|
309
|
+
if self.response_category not in allowed_response_categories:
|
|
310
|
+
message = (
|
|
311
|
+
f"Copilot Internal Roles request messages can only have of "
|
|
312
|
+
f"{', '.join(category.value for category in allowed_response_categories)}. " # noqa: E501
|
|
313
|
+
f"Got `{self.response_category}`."
|
|
314
|
+
)
|
|
249
315
|
structlogger.error(
|
|
250
|
-
"
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
316
|
+
"internal_copilot_request_chat_message.validate_response_category"
|
|
317
|
+
".invalid_response_category",
|
|
318
|
+
event_info=message,
|
|
319
|
+
response_category=self.response_category,
|
|
320
|
+
allowed_response_categories=allowed_response_categories,
|
|
255
321
|
role=self.role,
|
|
256
322
|
)
|
|
257
|
-
raise ValueError(
|
|
323
|
+
raise ValueError(message)
|
|
324
|
+
|
|
325
|
+
return self
|
|
326
|
+
|
|
327
|
+
def build_openai_message(self, prompt: str, *args, **kwargs) -> Dict[str, Any]: # type: ignore[no-untyped-def]
|
|
328
|
+
"""Build OpenAI message with pre-rendered prompt.
|
|
329
|
+
|
|
330
|
+
The prompt should be rendered externally using the content from this message
|
|
331
|
+
(logs, files, any additional context outside of this message, etc.) before
|
|
332
|
+
being passed to this method.
|
|
333
|
+
"""
|
|
334
|
+
return {"role": ROLE_USER, "content": prompt}
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
# Union type for all possible chat message types
|
|
338
|
+
ChatMessage = Union[
|
|
339
|
+
CopilotSystemMessage,
|
|
340
|
+
UserChatMessage,
|
|
341
|
+
CopilotChatMessage,
|
|
342
|
+
InternalCopilotRequestChatMessage,
|
|
343
|
+
]
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
class CopilotContext(BaseModel):
|
|
347
|
+
"""Model containing the context used by the copilot to generate a response."""
|
|
348
|
+
|
|
349
|
+
assistant_logs: str = Field(default="")
|
|
350
|
+
assistant_files: Dict[str, str] = Field(
|
|
351
|
+
default_factory=dict,
|
|
352
|
+
description=(
|
|
353
|
+
"The assistant files. Key is the file path, value is the file content."
|
|
354
|
+
),
|
|
355
|
+
)
|
|
356
|
+
copilot_chat_history: List[ChatMessage] = Field(default_factory=list)
|
|
357
|
+
tracker_context: Optional[TrackerContext] = Field(default=None)
|
|
358
|
+
|
|
359
|
+
class Config:
|
|
360
|
+
"""Config for LLMBuilderContext."""
|
|
258
361
|
|
|
259
|
-
|
|
362
|
+
arbitrary_types_allowed = True
|
|
260
363
|
|
|
261
364
|
|
|
262
365
|
class CopilotRequest(BaseModel):
|
|
263
366
|
"""Request model for the copilot endpoint."""
|
|
264
367
|
|
|
265
|
-
copilot_chat_history: List[
|
|
368
|
+
copilot_chat_history: List[ChatMessage] = Field(
|
|
266
369
|
...,
|
|
267
370
|
description=(
|
|
268
371
|
"The chat history between the user and the copilot. "
|
|
@@ -285,8 +388,43 @@ class CopilotRequest(BaseModel):
|
|
|
285
388
|
description='Signature scheme version (e.g. "v1").',
|
|
286
389
|
)
|
|
287
390
|
|
|
391
|
+
@field_validator("copilot_chat_history", mode="before")
|
|
392
|
+
@classmethod
|
|
393
|
+
def parse_chat_history(cls, v: List[Dict[str, Any]]) -> List[ChatMessage]:
|
|
394
|
+
"""Manually parse chat history messages based on role field."""
|
|
395
|
+
parsed_messages: List[ChatMessage] = []
|
|
396
|
+
available_roles = [ROLE_USER, ROLE_COPILOT, ROLE_COPILOT_INTERNAL]
|
|
397
|
+
for message_data in v:
|
|
398
|
+
role = message_data.get("role")
|
|
399
|
+
|
|
400
|
+
if role == ROLE_USER:
|
|
401
|
+
parsed_messages.append(UserChatMessage(**message_data))
|
|
402
|
+
|
|
403
|
+
elif role == ROLE_COPILOT:
|
|
404
|
+
parsed_messages.append(CopilotChatMessage(**message_data))
|
|
405
|
+
|
|
406
|
+
elif role == ROLE_COPILOT_INTERNAL:
|
|
407
|
+
parsed_messages.append(
|
|
408
|
+
InternalCopilotRequestChatMessage(**message_data)
|
|
409
|
+
)
|
|
410
|
+
|
|
411
|
+
else:
|
|
412
|
+
message = (
|
|
413
|
+
f"Unknown role '{role}' in chat message. "
|
|
414
|
+
f"Available roles are: {', '.join(available_roles)}."
|
|
415
|
+
)
|
|
416
|
+
structlogger.error(
|
|
417
|
+
"copilot_request.parse_chat_history.unknown_role",
|
|
418
|
+
event_info=message,
|
|
419
|
+
role=role,
|
|
420
|
+
available_roles=available_roles,
|
|
421
|
+
)
|
|
422
|
+
raise ValueError(message)
|
|
423
|
+
|
|
424
|
+
return parsed_messages
|
|
425
|
+
|
|
288
426
|
@property
|
|
289
|
-
def last_message(self) -> Optional[
|
|
427
|
+
def last_message(self) -> Optional[ChatMessage]:
|
|
290
428
|
"""Get the last message from the copilot chat history."""
|
|
291
429
|
if not self.copilot_chat_history:
|
|
292
430
|
return None
|
|
@@ -315,6 +453,12 @@ class CopilotOutput(BaseModel, ABC):
|
|
|
315
453
|
"""Convert to SSE event format."""
|
|
316
454
|
pass
|
|
317
455
|
|
|
456
|
+
@property
|
|
457
|
+
@abstractmethod
|
|
458
|
+
def sse_data(self) -> Dict[str, Any]:
|
|
459
|
+
"""Extract the SSE data payload."""
|
|
460
|
+
pass
|
|
461
|
+
|
|
318
462
|
|
|
319
463
|
class GeneratedContent(CopilotOutput):
|
|
320
464
|
"""Represents generated content from the LLM to be streamed."""
|
|
@@ -327,13 +471,18 @@ class GeneratedContent(CopilotOutput):
|
|
|
327
471
|
"""Convert to SSE event format."""
|
|
328
472
|
return ServerSentEvent(
|
|
329
473
|
event="copilot_response",
|
|
330
|
-
data=
|
|
331
|
-
"content": self.content,
|
|
332
|
-
"response_category": self.response_category.value,
|
|
333
|
-
"completeness": self.response_completeness.value,
|
|
334
|
-
},
|
|
474
|
+
data=self.sse_data,
|
|
335
475
|
)
|
|
336
476
|
|
|
477
|
+
@property
|
|
478
|
+
def sse_data(self) -> Dict[str, Any]:
|
|
479
|
+
"""Extract the SSE data payload."""
|
|
480
|
+
return {
|
|
481
|
+
"content": self.content,
|
|
482
|
+
"response_category": self.response_category.value,
|
|
483
|
+
"completeness": self.response_completeness.value,
|
|
484
|
+
}
|
|
485
|
+
|
|
337
486
|
|
|
338
487
|
class ReferenceEntry(CopilotOutput):
|
|
339
488
|
"""Represents a reference entry with title and url."""
|
|
@@ -361,15 +510,20 @@ class ReferenceEntry(CopilotOutput):
|
|
|
361
510
|
"""Convert to SSE event format."""
|
|
362
511
|
return ServerSentEvent(
|
|
363
512
|
event="copilot_response",
|
|
364
|
-
data=
|
|
365
|
-
"index": self.index,
|
|
366
|
-
"title": self.title,
|
|
367
|
-
"url": self.url,
|
|
368
|
-
"response_category": self.response_category.value,
|
|
369
|
-
"completeness": self.response_completeness.value,
|
|
370
|
-
},
|
|
513
|
+
data=self.sse_data,
|
|
371
514
|
)
|
|
372
515
|
|
|
516
|
+
@property
|
|
517
|
+
def sse_data(self) -> Dict[str, Any]:
|
|
518
|
+
"""Extract the SSE data payload."""
|
|
519
|
+
return {
|
|
520
|
+
"index": self.index,
|
|
521
|
+
"title": self.title,
|
|
522
|
+
"url": self.url,
|
|
523
|
+
"response_category": self.response_category.value,
|
|
524
|
+
"completeness": self.response_completeness.value,
|
|
525
|
+
}
|
|
526
|
+
|
|
373
527
|
|
|
374
528
|
class ReferenceSection(CopilotOutput):
|
|
375
529
|
"""Represents a reference section with documentation links."""
|
|
@@ -395,16 +549,21 @@ class ReferenceSection(CopilotOutput):
|
|
|
395
549
|
"""Convert to SSE event format."""
|
|
396
550
|
return ServerSentEvent(
|
|
397
551
|
event="copilot_response",
|
|
398
|
-
data=
|
|
399
|
-
"references": [
|
|
400
|
-
reference.model_dump(include={"index", "title", "url"})
|
|
401
|
-
for reference in self.references
|
|
402
|
-
],
|
|
403
|
-
"response_category": self.response_category.value,
|
|
404
|
-
"completeness": self.response_completeness.value,
|
|
405
|
-
},
|
|
552
|
+
data=self.sse_data,
|
|
406
553
|
)
|
|
407
554
|
|
|
555
|
+
@property
|
|
556
|
+
def sse_data(self) -> Dict[str, Any]:
|
|
557
|
+
"""Extract the SSE data payload."""
|
|
558
|
+
return {
|
|
559
|
+
"references": [
|
|
560
|
+
reference.model_dump(include={"index", "title", "url"})
|
|
561
|
+
for reference in self.references
|
|
562
|
+
],
|
|
563
|
+
"response_category": self.response_category.value,
|
|
564
|
+
"completeness": self.response_completeness.value,
|
|
565
|
+
}
|
|
566
|
+
|
|
408
567
|
def sort_references(self) -> None:
|
|
409
568
|
"""Sort references by index value."""
|
|
410
569
|
sorted_references = sorted(
|
|
@@ -414,18 +573,42 @@ class ReferenceSection(CopilotOutput):
|
|
|
414
573
|
self.references = sorted_references
|
|
415
574
|
|
|
416
575
|
|
|
417
|
-
class
|
|
418
|
-
"""
|
|
576
|
+
class TrainingErrorLog(CopilotOutput):
|
|
577
|
+
"""Represents an error log."""
|
|
419
578
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
579
|
+
logs: List[LogContent]
|
|
580
|
+
response_category: ResponseCategory = Field(
|
|
581
|
+
default=ResponseCategory.TRAINING_ERROR_LOG,
|
|
582
|
+
frozen=True,
|
|
583
|
+
)
|
|
584
|
+
response_completeness: ResponseCompleteness = ResponseCompleteness.COMPLETE
|
|
424
585
|
|
|
425
|
-
|
|
426
|
-
|
|
586
|
+
@model_validator(mode="after")
|
|
587
|
+
def validate_response_category(self) -> "TrainingErrorLog":
|
|
588
|
+
"""Validate that response_category has the correct default value."""
|
|
589
|
+
if self.response_category != ResponseCategory.TRAINING_ERROR_LOG:
|
|
590
|
+
raise ValueError(
|
|
591
|
+
f"TrainingErrorLog response_category must be "
|
|
592
|
+
f"{ResponseCategory.TRAINING_ERROR_LOG}, "
|
|
593
|
+
f"got `{self.response_category}`."
|
|
594
|
+
)
|
|
595
|
+
return self
|
|
427
596
|
|
|
428
|
-
|
|
597
|
+
def to_sse_event(self) -> ServerSentEvent:
|
|
598
|
+
"""Convert to SSE event format."""
|
|
599
|
+
return ServerSentEvent(
|
|
600
|
+
event="copilot_response",
|
|
601
|
+
data=self.sse_data,
|
|
602
|
+
)
|
|
603
|
+
|
|
604
|
+
@property
|
|
605
|
+
def sse_data(self) -> Dict[str, Any]:
|
|
606
|
+
"""Extract the SSE data payload."""
|
|
607
|
+
return {
|
|
608
|
+
"logs": [log.model_dump() for log in self.logs],
|
|
609
|
+
"response_category": self.response_category.value,
|
|
610
|
+
"completeness": self.response_completeness.value,
|
|
611
|
+
}
|
|
429
612
|
|
|
430
613
|
|
|
431
614
|
class UsageStatistics(BaseModel):
|
|
@@ -493,6 +676,13 @@ class CopilotGenerationContext(BaseModel):
|
|
|
493
676
|
last_user_message: Optional[Dict[str, Any]] = Field(
|
|
494
677
|
None, description="The last user message with context that was processed."
|
|
495
678
|
)
|
|
679
|
+
tracker_event_attachments: List[EventContent] = Field(
|
|
680
|
+
...,
|
|
681
|
+
description=(
|
|
682
|
+
"The tracker event attachments passed with the user message used as "
|
|
683
|
+
"an additional context."
|
|
684
|
+
),
|
|
685
|
+
)
|
|
496
686
|
|
|
497
687
|
class Config:
|
|
498
688
|
"""Config for CopilotGenerationContext."""
|
|
@@ -86,9 +86,9 @@ and panels.
|
|
|
86
86
|
***
|
|
87
87
|
|
|
88
88
|
## Layout
|
|
89
|
-
- **Left Panel
|
|
90
|
-
- **Center Panel
|
|
91
|
-
- **Right Panel
|
|
89
|
+
- **Left Panel - Copilot Chat:** Where the user asks you for help, guidance, or troubleshooting.
|
|
90
|
+
- **Center Panel - Playground Preview:** Main workspace with Chat Mode (default) or Inspect Mode.
|
|
91
|
+
- **Right Panel - Inspector Visualization:** Real-time diagram of conversation logic (only in Inspect Mode).
|
|
92
92
|
|
|
93
93
|
***
|
|
94
94
|
|
|
@@ -140,19 +140,40 @@ response.
|
|
|
140
140
|
|
|
141
141
|
***
|
|
142
142
|
|
|
143
|
+
### 5. Sharing Attachments
|
|
144
|
+
|
|
145
|
+
Users can share additional context with Copilot by clicking the "Ask Copilot" button
|
|
146
|
+
while "Inspect Mode" is open. This sends selected conversation state as an attachment
|
|
147
|
+
together with their question.
|
|
148
|
+
|
|
149
|
+
The attachments are typically tracker events, which can be:
|
|
150
|
+
- User messages - what the user typed or said.
|
|
151
|
+
- Assistant messages - what the assistant responded with.
|
|
152
|
+
- Actions - operations the assistant executed, including how they were chosen.
|
|
153
|
+
- Slots - what slots were set or updated during the exchange.
|
|
154
|
+
- Flows - when a flow starts, is interrupted, resumes, or completes.
|
|
155
|
+
- Sessions - the beginning or end of a conversation session.
|
|
156
|
+
|
|
157
|
+
**Tip:** Encourage users to use attachments to get to know the Rasa workings better. If
|
|
158
|
+
user is facing issues, these attachments will give Copilot a ground-truth trace of what
|
|
159
|
+
actually happened in the assistant, making attachments a powerful tool for debugging.
|
|
160
|
+
|
|
161
|
+
***
|
|
162
|
+
|
|
143
163
|
## Rasa CLI to Hello Rasa UI Mapping
|
|
144
164
|
|
|
145
165
|
Map available features to **Rasa CLI** to the **Hello Rasa Action**, so users see
|
|
146
166
|
continuity.
|
|
147
167
|
|
|
148
|
-
| Feature
|
|
149
|
-
|
|
150
|
-
| Train assistant
|
|
151
|
-
| Test conversation
|
|
152
|
-
| Debug conversation
|
|
153
|
-
| Run custom actions
|
|
154
|
-
| Export project files
|
|
155
|
-
| Edit project files
|
|
168
|
+
| Feature | Rasa CLI | Hello Rasa Action |
|
|
169
|
+
|--------------------------|-----------------------|------------------------------------|
|
|
170
|
+
| Train assistant | `rasa train` | Apply Changes |
|
|
171
|
+
| Test conversation | `rasa shell` | Chat Mode |
|
|
172
|
+
| Debug conversation | `rasa shell --debug` | Inspect Mode |
|
|
173
|
+
| Run custom actions | `rasa run actions` | Code Mode + Apply Changes |
|
|
174
|
+
| Export project files | — | Download button |
|
|
175
|
+
| Edit project files | — | Code Mode |
|
|
176
|
+
| Share conversation trace | `rasa shell --debug` | Ask Copilot button in Inspect Mode |
|
|
156
177
|
|
|
157
178
|
**Note:** Ignore any references to *Rasa Studio*.
|
|
158
179
|
|
|
@@ -181,7 +202,7 @@ code change, or reference). It should be:
|
|
|
181
202
|
- **Friendly, but Focused**: Use a warm and conversational style, but stay precise and technically correct.
|
|
182
203
|
- **Confident & Trustworthy**: Present guidance as clear and reliable; avoid hedging unless there's genuine uncertainty (in which case, ask clarifying questions).
|
|
183
204
|
- **Brand-Positive**: Highlight the strengths of **Rasa** and **Hello Rasa**, when appropriate, framing them as powerful and easy to use.
|
|
184
|
-
- **Code-style references
|
|
205
|
+
- **Code-style references**: You MUST wrap all flow names, slot names, variables, and any part of the user's code in backticks (e.g., `slot_name`, `flow_name`, `variable_name`). This is mandatory formatting.
|
|
185
206
|
|
|
186
207
|
***
|
|
187
208
|
|