rasa-pro 3.12.5__py3-none-any.whl → 3.13.0.dev1__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/cli/scaffold.py +1 -1
- rasa/core/actions/action.py +38 -28
- rasa/core/actions/action_run_slot_rejections.py +1 -1
- rasa/core/channels/studio_chat.py +16 -43
- rasa/core/information_retrieval/faiss.py +62 -6
- rasa/core/nlg/contextual_response_rephraser.py +7 -6
- rasa/core/nlg/generator.py +5 -21
- rasa/core/nlg/response.py +6 -43
- rasa/core/nlg/translate.py +0 -8
- rasa/core/policies/enterprise_search_policy.py +1 -0
- rasa/dialogue_understanding/commands/knowledge_answer_command.py +2 -2
- rasa/dialogue_understanding/generator/llm_based_command_generator.py +1 -2
- rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_claude_3_5_sonnet_20240620_template.jinja2 +1 -1
- rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_gpt_4o_2024_11_20_template.jinja2 +5 -2
- rasa/dialogue_understanding_test/command_metric_calculation.py +7 -40
- rasa/dialogue_understanding_test/command_metrics.py +38 -0
- rasa/dialogue_understanding_test/du_test_case.py +58 -25
- rasa/dialogue_understanding_test/du_test_result.py +228 -132
- rasa/dialogue_understanding_test/du_test_runner.py +10 -1
- rasa/dialogue_understanding_test/io.py +35 -8
- rasa/model_manager/model_api.py +1 -1
- rasa/model_manager/socket_bridge.py +0 -7
- rasa/shared/core/slot_mappings.py +12 -0
- rasa/shared/core/slots.py +1 -1
- rasa/shared/core/trackers.py +4 -10
- rasa/shared/providers/llm/default_litellm_llm_client.py +2 -2
- rasa/tracing/instrumentation/attribute_extractors.py +36 -6
- rasa/version.py +1 -1
- {rasa_pro-3.12.5.dist-info → rasa_pro-3.13.0.dev1.dist-info}/METADATA +5 -6
- {rasa_pro-3.12.5.dist-info → rasa_pro-3.13.0.dev1.dist-info}/RECORD +33 -34
- {rasa_pro-3.12.5.dist-info → rasa_pro-3.13.0.dev1.dist-info}/WHEEL +1 -1
- README.md +0 -38
- rasa/keys +0 -1
- {rasa_pro-3.12.5.dist-info → rasa_pro-3.13.0.dev1.dist-info}/NOTICE +0 -0
- {rasa_pro-3.12.5.dist-info → rasa_pro-3.13.0.dev1.dist-info}/entry_points.txt +0 -0
|
@@ -1,54 +1,21 @@
|
|
|
1
|
+
import typing
|
|
1
2
|
from collections import defaultdict
|
|
2
3
|
from typing import Dict, List
|
|
3
4
|
|
|
4
|
-
from pydantic import BaseModel
|
|
5
|
-
|
|
6
5
|
from rasa.dialogue_understanding.commands import Command
|
|
7
6
|
from rasa.dialogue_understanding_test.command_comparison import (
|
|
8
7
|
is_command_present_in_list,
|
|
9
8
|
)
|
|
10
|
-
from rasa.dialogue_understanding_test.
|
|
11
|
-
DialogueUnderstandingTestResult,
|
|
12
|
-
)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class CommandMetrics(BaseModel):
|
|
16
|
-
tp: int
|
|
17
|
-
fp: int
|
|
18
|
-
fn: int
|
|
19
|
-
total_count: int
|
|
20
|
-
|
|
21
|
-
@staticmethod
|
|
22
|
-
def _safe_divide(numerator: float, denominator: float) -> float:
|
|
23
|
-
"""Safely perform division, returning 0.0 if the denominator is zero."""
|
|
24
|
-
return numerator / denominator if denominator > 0 else 0.0
|
|
9
|
+
from rasa.dialogue_understanding_test.command_metrics import CommandMetrics
|
|
25
10
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
return self._safe_divide(self.tp, self.tp + self.fn)
|
|
31
|
-
|
|
32
|
-
def get_f1_score(self) -> float:
|
|
33
|
-
precision = self.get_precision()
|
|
34
|
-
recall = self.get_recall()
|
|
35
|
-
|
|
36
|
-
return self._safe_divide(2 * precision * recall, precision + recall)
|
|
37
|
-
|
|
38
|
-
def as_dict(self) -> Dict[str, float]:
|
|
39
|
-
return {
|
|
40
|
-
"tp": self.tp,
|
|
41
|
-
"fp": self.fp,
|
|
42
|
-
"fn": self.fn,
|
|
43
|
-
"precision": self.get_precision(),
|
|
44
|
-
"recall": self.get_recall(),
|
|
45
|
-
"f1_score": self.get_f1_score(),
|
|
46
|
-
"total_count": self.total_count,
|
|
47
|
-
}
|
|
11
|
+
if typing.TYPE_CHECKING:
|
|
12
|
+
from rasa.dialogue_understanding_test.du_test_result import (
|
|
13
|
+
DialogueUnderstandingTestResult,
|
|
14
|
+
)
|
|
48
15
|
|
|
49
16
|
|
|
50
17
|
def calculate_command_metrics(
|
|
51
|
-
test_results: List[DialogueUnderstandingTestResult],
|
|
18
|
+
test_results: List["DialogueUnderstandingTestResult"],
|
|
52
19
|
) -> Dict[str, CommandMetrics]:
|
|
53
20
|
"""Calculate the command metrics for the test result."""
|
|
54
21
|
metrics: Dict[str, CommandMetrics] = defaultdict(
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from typing import Dict
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class CommandMetrics(BaseModel):
|
|
7
|
+
tp: int
|
|
8
|
+
fp: int
|
|
9
|
+
fn: int
|
|
10
|
+
total_count: int
|
|
11
|
+
|
|
12
|
+
@staticmethod
|
|
13
|
+
def _safe_divide(numerator: float, denominator: float) -> float:
|
|
14
|
+
"""Safely perform division, returning 0.0 if the denominator is zero."""
|
|
15
|
+
return numerator / denominator if denominator > 0 else 0.0
|
|
16
|
+
|
|
17
|
+
def get_precision(self) -> float:
|
|
18
|
+
return self._safe_divide(self.tp, self.tp + self.fp)
|
|
19
|
+
|
|
20
|
+
def get_recall(self) -> float:
|
|
21
|
+
return self._safe_divide(self.tp, self.tp + self.fn)
|
|
22
|
+
|
|
23
|
+
def get_f1_score(self) -> float:
|
|
24
|
+
precision = self.get_precision()
|
|
25
|
+
recall = self.get_recall()
|
|
26
|
+
|
|
27
|
+
return self._safe_divide(2 * precision * recall, precision + recall)
|
|
28
|
+
|
|
29
|
+
def as_dict(self) -> Dict[str, float]:
|
|
30
|
+
return {
|
|
31
|
+
"tp": self.tp,
|
|
32
|
+
"fp": self.fp,
|
|
33
|
+
"fn": self.fn,
|
|
34
|
+
"precision": self.get_precision(),
|
|
35
|
+
"recall": self.get_recall(),
|
|
36
|
+
"f1_score": self.get_f1_score(),
|
|
37
|
+
"total_count": self.total_count,
|
|
38
|
+
}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
from collections import defaultdict
|
|
1
2
|
from typing import Any, Dict, Iterator, List, Optional, Tuple
|
|
2
3
|
|
|
3
4
|
from pydantic import BaseModel, Field
|
|
4
5
|
|
|
6
|
+
from rasa.core import IntentlessPolicy
|
|
7
|
+
from rasa.core.nlg.contextual_response_rephraser import ContextualResponseRephraser
|
|
8
|
+
from rasa.core.policies.enterprise_search_policy import EnterpriseSearchPolicy
|
|
5
9
|
from rasa.dialogue_understanding.commands.prompt_command import PromptCommand
|
|
6
10
|
from rasa.dialogue_understanding.generator.command_parser import parse_commands
|
|
7
11
|
from rasa.dialogue_understanding_test.command_comparison import are_command_lists_equal
|
|
@@ -69,6 +73,8 @@ class DialogueUnderstandingOutput(BaseModel):
|
|
|
69
73
|
commands: Dict[str, List[PromptCommand]]
|
|
70
74
|
# List of prompts
|
|
71
75
|
prompts: Optional[List[Dict[str, Any]]] = None
|
|
76
|
+
# Latency of the full message roundtrip
|
|
77
|
+
latency: Optional[float] = None
|
|
72
78
|
|
|
73
79
|
class Config:
|
|
74
80
|
"""Skip validation for PromptCommand protocol as pydantic does not know how to
|
|
@@ -88,27 +94,41 @@ class DialogueUnderstandingOutput(BaseModel):
|
|
|
88
94
|
def get_component_names_that_predicted_commands_or_have_llm_response(
|
|
89
95
|
self,
|
|
90
96
|
) -> List[str]:
|
|
91
|
-
"""Get all component names
|
|
97
|
+
"""Get all relevant component names.
|
|
98
|
+
|
|
99
|
+
Components are relevant if they have predicted commands or received a
|
|
92
100
|
non-empty response from LLM.
|
|
93
101
|
"""
|
|
102
|
+
# Exclude components that are not related to Dialogue Understanding
|
|
103
|
+
component_names_to_exclude = [
|
|
104
|
+
EnterpriseSearchPolicy.__name__,
|
|
105
|
+
IntentlessPolicy.__name__,
|
|
106
|
+
ContextualResponseRephraser.__name__,
|
|
107
|
+
]
|
|
108
|
+
|
|
94
109
|
component_names_that_predicted_commands = (
|
|
95
110
|
[
|
|
96
111
|
component_name
|
|
97
112
|
for component_name, predicted_commands in self.commands.items()
|
|
98
113
|
if predicted_commands
|
|
114
|
+
and component_name not in component_names_to_exclude
|
|
99
115
|
]
|
|
100
116
|
if self.commands
|
|
101
117
|
else []
|
|
102
118
|
)
|
|
119
|
+
|
|
103
120
|
components_with_prompts = (
|
|
104
121
|
[
|
|
105
122
|
str(prompt.get(KEY_COMPONENT_NAME, None))
|
|
106
123
|
for prompt in self.prompts
|
|
107
124
|
if prompt.get(KEY_LLM_RESPONSE_METADATA, None)
|
|
125
|
+
and prompt.get(KEY_COMPONENT_NAME, None)
|
|
126
|
+
not in component_names_to_exclude
|
|
108
127
|
]
|
|
109
128
|
if self.prompts
|
|
110
129
|
else []
|
|
111
130
|
)
|
|
131
|
+
|
|
112
132
|
return list(
|
|
113
133
|
set(component_names_that_predicted_commands + components_with_prompts)
|
|
114
134
|
)
|
|
@@ -290,41 +310,54 @@ class DialogueUnderstandingTestStep(BaseModel):
|
|
|
290
310
|
|
|
291
311
|
return ""
|
|
292
312
|
|
|
293
|
-
def get_latencies(self) -> List[float]:
|
|
313
|
+
def get_latencies(self) -> Dict[str, List[float]]:
|
|
294
314
|
if self.dialogue_understanding_output is None:
|
|
295
|
-
return
|
|
315
|
+
return {}
|
|
296
316
|
|
|
297
|
-
|
|
317
|
+
component_name_to_prompt_info = (
|
|
318
|
+
self.dialogue_understanding_output.get_component_name_to_prompt_info()
|
|
319
|
+
)
|
|
298
320
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
for
|
|
302
|
-
|
|
303
|
-
]
|
|
321
|
+
latencies = defaultdict(list)
|
|
322
|
+
for component_name, prompt_info_list in component_name_to_prompt_info.items():
|
|
323
|
+
for prompt_info in prompt_info_list:
|
|
324
|
+
latencies[component_name].append(prompt_info.get(KEY_LATENCY, 0.0))
|
|
304
325
|
|
|
305
|
-
|
|
326
|
+
return latencies
|
|
327
|
+
|
|
328
|
+
def get_completion_tokens(self) -> Dict[str, List[float]]:
|
|
306
329
|
if self.dialogue_understanding_output is None:
|
|
307
|
-
return
|
|
330
|
+
return {}
|
|
308
331
|
|
|
309
|
-
|
|
332
|
+
component_name_to_prompt_info = (
|
|
333
|
+
self.dialogue_understanding_output.get_component_name_to_prompt_info()
|
|
334
|
+
)
|
|
310
335
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
for
|
|
314
|
-
|
|
315
|
-
|
|
336
|
+
completion_tokens = defaultdict(list)
|
|
337
|
+
for component_name, prompt_info_list in component_name_to_prompt_info.items():
|
|
338
|
+
for prompt_info in prompt_info_list:
|
|
339
|
+
completion_tokens[component_name].append(
|
|
340
|
+
prompt_info.get(KEY_COMPLETION_TOKENS, 0.0)
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
return completion_tokens
|
|
316
344
|
|
|
317
|
-
def get_prompt_tokens(self) -> List[
|
|
345
|
+
def get_prompt_tokens(self) -> Dict[str, List[float]]:
|
|
318
346
|
if self.dialogue_understanding_output is None:
|
|
319
|
-
return
|
|
347
|
+
return {}
|
|
320
348
|
|
|
321
|
-
|
|
349
|
+
component_name_to_prompt_info = (
|
|
350
|
+
self.dialogue_understanding_output.get_component_name_to_prompt_info()
|
|
351
|
+
)
|
|
322
352
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
for
|
|
326
|
-
|
|
327
|
-
|
|
353
|
+
prompt_tokens = defaultdict(list)
|
|
354
|
+
for component_name, prompt_info_list in component_name_to_prompt_info.items():
|
|
355
|
+
for prompt_info in prompt_info_list:
|
|
356
|
+
prompt_tokens[component_name].append(
|
|
357
|
+
prompt_info.get(KEY_PROMPT_TOKENS, 0.0)
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
return prompt_tokens
|
|
328
361
|
|
|
329
362
|
|
|
330
363
|
class DialogueUnderstandingTestCase(BaseModel):
|