rasa-pro 3.12.13__py3-none-any.whl → 3.12.15__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/llm_fine_tuning.py +11 -10
- rasa/core/nlg/contextual_response_rephraser.py +38 -11
- rasa/core/nlg/summarize.py +39 -5
- rasa/core/policies/enterprise_search_policy.py +7 -4
- rasa/core/policies/intentless_policy.py +15 -9
- rasa/core/utils.py +4 -0
- rasa/dialogue_understanding/coexistence/llm_based_router.py +8 -3
- rasa/dialogue_understanding/commands/clarify_command.py +1 -1
- rasa/dialogue_understanding/commands/set_slot_command.py +1 -1
- rasa/dialogue_understanding/generator/constants.py +2 -2
- rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +2 -2
- rasa/dialogue_understanding/processor/command_processor_component.py +2 -2
- rasa/dialogue_understanding_test/du_test_runner.py +3 -21
- rasa/dialogue_understanding_test/test_case_simulation/test_case_tracker_simulator.py +2 -6
- rasa/engine/recipes/default_recipe.py +26 -2
- rasa/llm_fine_tuning/annotation_module.py +39 -9
- rasa/llm_fine_tuning/conversations.py +3 -0
- rasa/llm_fine_tuning/llm_data_preparation_module.py +66 -49
- rasa/llm_fine_tuning/paraphrasing/conversation_rephraser.py +4 -2
- rasa/llm_fine_tuning/paraphrasing/rephrase_validator.py +52 -44
- rasa/llm_fine_tuning/paraphrasing_module.py +10 -12
- rasa/llm_fine_tuning/storage.py +4 -4
- rasa/llm_fine_tuning/utils.py +63 -1
- rasa/shared/constants.py +3 -0
- rasa/shared/exceptions.py +4 -0
- rasa/shared/providers/_configs/azure_openai_client_config.py +4 -0
- rasa/shared/providers/_configs/openai_client_config.py +4 -0
- rasa/shared/providers/embedding/_base_litellm_embedding_client.py +3 -0
- rasa/shared/providers/llm/_base_litellm_client.py +5 -2
- rasa/shared/utils/llm.py +36 -3
- rasa/version.py +1 -1
- {rasa_pro-3.12.13.dist-info → rasa_pro-3.12.15.dist-info}/METADATA +1 -1
- {rasa_pro-3.12.13.dist-info → rasa_pro-3.12.15.dist-info}/RECORD +36 -36
- {rasa_pro-3.12.13.dist-info → rasa_pro-3.12.15.dist-info}/NOTICE +0 -0
- {rasa_pro-3.12.13.dist-info → rasa_pro-3.12.15.dist-info}/WHEEL +0 -0
- {rasa_pro-3.12.13.dist-info → rasa_pro-3.12.15.dist-info}/entry_points.txt +0 -0
rasa/cli/llm_fine_tuning.py
CHANGED
|
@@ -208,10 +208,7 @@ def prepare_llm_fine_tuning_data(args: argparse.Namespace) -> None:
|
|
|
208
208
|
sys.exit(0)
|
|
209
209
|
|
|
210
210
|
flows = asyncio.run(e2e_test_runner.agent.processor.get_flows())
|
|
211
|
-
|
|
212
|
-
llm_command_generator: Type[LLMBasedCommandGenerator] = _get_llm_command_generator(
|
|
213
|
-
e2e_test_runner
|
|
214
|
-
)
|
|
211
|
+
_validate_llm_command_generator_present(e2e_test_runner)
|
|
215
212
|
|
|
216
213
|
# set up storage context
|
|
217
214
|
storage_context = create_storage_context(StorageType.FILE, output_dir)
|
|
@@ -242,11 +239,11 @@ def prepare_llm_fine_tuning_data(args: argparse.Namespace) -> None:
|
|
|
242
239
|
rephrase_config,
|
|
243
240
|
args.num_rephrases,
|
|
244
241
|
flows,
|
|
245
|
-
|
|
246
|
-
llm_command_generator_config,
|
|
242
|
+
e2e_test_runner.agent,
|
|
247
243
|
storage_context,
|
|
248
244
|
)
|
|
249
245
|
)
|
|
246
|
+
|
|
250
247
|
statistics["num_passing_rephrased_user_messages"] = sum(
|
|
251
248
|
[conversation.get_number_of_rephrases(True) for conversation in conversations]
|
|
252
249
|
)
|
|
@@ -257,7 +254,11 @@ def prepare_llm_fine_tuning_data(args: argparse.Namespace) -> None:
|
|
|
257
254
|
|
|
258
255
|
# 3. create fine-tuning dataset
|
|
259
256
|
log_start_of_module("LLM Data Preparation")
|
|
260
|
-
llm_fine_tuning_data =
|
|
257
|
+
llm_fine_tuning_data = asyncio.run(
|
|
258
|
+
convert_to_fine_tuning_data(
|
|
259
|
+
conversations, storage_context, e2e_test_runner.agent
|
|
260
|
+
)
|
|
261
|
+
)
|
|
261
262
|
statistics["num_ft_data_points"] = len(llm_fine_tuning_data)
|
|
262
263
|
log_end_of_module("LLM Data Preparation", statistics)
|
|
263
264
|
|
|
@@ -311,9 +312,9 @@ def _get_llm_command_generator_config(e2e_test_runner: E2ETestRunner) -> Dict[st
|
|
|
311
312
|
sys.exit(1)
|
|
312
313
|
|
|
313
314
|
|
|
314
|
-
def
|
|
315
|
+
def _validate_llm_command_generator_present(
|
|
315
316
|
e2e_test_runner: E2ETestRunner,
|
|
316
|
-
) ->
|
|
317
|
+
) -> None:
|
|
317
318
|
train_schema = e2e_test_runner.agent.processor.model_metadata.train_schema # type: ignore
|
|
318
319
|
|
|
319
320
|
for _, node in train_schema.nodes.items():
|
|
@@ -322,7 +323,7 @@ def _get_llm_command_generator(
|
|
|
322
323
|
) and not node.matches_type(
|
|
323
324
|
MultiStepLLMCommandGenerator, include_subtypes=True
|
|
324
325
|
):
|
|
325
|
-
return
|
|
326
|
+
return
|
|
326
327
|
|
|
327
328
|
rasa.shared.utils.cli.print_error(
|
|
328
329
|
"The provided model is not trained using 'SingleStepLLMCommandGenerator' or "
|
|
@@ -5,15 +5,20 @@ from jinja2 import Template
|
|
|
5
5
|
|
|
6
6
|
from rasa import telemetry
|
|
7
7
|
from rasa.core.nlg.response import TemplatedNaturalLanguageGenerator
|
|
8
|
-
from rasa.core.nlg.summarize import
|
|
8
|
+
from rasa.core.nlg.summarize import (
|
|
9
|
+
_count_multiple_utterances_as_single_turn,
|
|
10
|
+
summarize_conversation,
|
|
11
|
+
)
|
|
9
12
|
from rasa.shared.constants import (
|
|
10
13
|
LLM_CONFIG_KEY,
|
|
14
|
+
MAX_COMPLETION_TOKENS_CONFIG_KEY,
|
|
11
15
|
MODEL_CONFIG_KEY,
|
|
12
16
|
MODEL_GROUP_ID_CONFIG_KEY,
|
|
13
17
|
MODEL_NAME_CONFIG_KEY,
|
|
14
18
|
OPENAI_PROVIDER,
|
|
15
19
|
PROMPT_CONFIG_KEY,
|
|
16
20
|
PROVIDER_CONFIG_KEY,
|
|
21
|
+
TEMPERATURE_CONFIG_KEY,
|
|
17
22
|
TIMEOUT_CONFIG_KEY,
|
|
18
23
|
)
|
|
19
24
|
from rasa.shared.core.domain import KEY_RESPONSES_TEXT, Domain
|
|
@@ -53,12 +58,13 @@ RESPONSE_SUMMARISE_CONVERSATION_KEY = "summarize_conversation"
|
|
|
53
58
|
DEFAULT_REPHRASE_ALL = False
|
|
54
59
|
DEFAULT_SUMMARIZE_HISTORY = True
|
|
55
60
|
DEFAULT_MAX_HISTORICAL_TURNS = 5
|
|
61
|
+
DEFAULT_COUNT_MULTIPLE_UTTERANCES_AS_SINGLE_TURN = True
|
|
56
62
|
|
|
57
63
|
DEFAULT_LLM_CONFIG = {
|
|
58
64
|
PROVIDER_CONFIG_KEY: OPENAI_PROVIDER,
|
|
59
65
|
MODEL_CONFIG_KEY: DEFAULT_OPENAI_GENERATE_MODEL_NAME,
|
|
60
|
-
|
|
61
|
-
|
|
66
|
+
TEMPERATURE_CONFIG_KEY: 0.3,
|
|
67
|
+
MAX_COMPLETION_TOKENS_CONFIG_KEY: DEFAULT_OPENAI_MAX_GENERATED_TOKENS,
|
|
62
68
|
TIMEOUT_CONFIG_KEY: 5,
|
|
63
69
|
}
|
|
64
70
|
|
|
@@ -70,6 +76,7 @@ its meaning. Use simple {{language}}.
|
|
|
70
76
|
Context / previous conversation with the user:
|
|
71
77
|
{{history}}
|
|
72
78
|
|
|
79
|
+
Last user message:
|
|
73
80
|
{{current_input}}
|
|
74
81
|
|
|
75
82
|
Suggested AI Response: {{suggested_response}}
|
|
@@ -122,6 +129,11 @@ class ContextualResponseRephraser(
|
|
|
122
129
|
"max_historical_turns", DEFAULT_MAX_HISTORICAL_TURNS
|
|
123
130
|
)
|
|
124
131
|
|
|
132
|
+
self.count_multiple_utterances_as_single_turn = self.nlg_endpoint.kwargs.get(
|
|
133
|
+
"count_multiple_utterances_as_single_turn",
|
|
134
|
+
DEFAULT_COUNT_MULTIPLE_UTTERANCES_AS_SINGLE_TURN,
|
|
135
|
+
)
|
|
136
|
+
|
|
125
137
|
self.llm_config = resolve_model_client_config(
|
|
126
138
|
self.nlg_endpoint.kwargs.get(LLM_CONFIG_KEY),
|
|
127
139
|
ContextualResponseRephraser.__name__,
|
|
@@ -260,8 +272,16 @@ class ContextualResponseRephraser(
|
|
|
260
272
|
Returns:
|
|
261
273
|
The history for the prompt.
|
|
262
274
|
"""
|
|
275
|
+
# Count multiple utterances by bot/user as single turn in conversation history
|
|
276
|
+
turns_wrapper = (
|
|
277
|
+
_count_multiple_utterances_as_single_turn
|
|
278
|
+
if self.count_multiple_utterances_as_single_turn
|
|
279
|
+
else None
|
|
280
|
+
)
|
|
263
281
|
llm = llm_factory(self.llm_config, DEFAULT_LLM_CONFIG)
|
|
264
|
-
return await summarize_conversation(
|
|
282
|
+
return await summarize_conversation(
|
|
283
|
+
tracker, llm, max_turns=5, turns_wrapper=turns_wrapper
|
|
284
|
+
)
|
|
265
285
|
|
|
266
286
|
async def rephrase(
|
|
267
287
|
self,
|
|
@@ -283,19 +303,26 @@ class ContextualResponseRephraser(
|
|
|
283
303
|
|
|
284
304
|
prompt_template_text = self._template_for_response_rephrasing(response)
|
|
285
305
|
|
|
286
|
-
#
|
|
287
|
-
|
|
288
|
-
current_input =
|
|
306
|
+
# Last user message (=current input) should always be in prompt if available
|
|
307
|
+
last_message_by_user = getattr(tracker.latest_message, "text", "")
|
|
308
|
+
current_input = (
|
|
309
|
+
f"{USER}: {last_message_by_user}" if last_message_by_user else ""
|
|
310
|
+
)
|
|
289
311
|
|
|
290
312
|
# Only summarise conversation history if flagged
|
|
291
313
|
if self.summarize_history:
|
|
292
314
|
history = await self._create_history(tracker)
|
|
293
315
|
else:
|
|
294
|
-
#
|
|
316
|
+
# Count multiple utterances by bot/user as single turn
|
|
317
|
+
turns_wrapper = (
|
|
318
|
+
_count_multiple_utterances_as_single_turn
|
|
319
|
+
if self.count_multiple_utterances_as_single_turn
|
|
320
|
+
else None
|
|
321
|
+
)
|
|
295
322
|
max_turns = max(self.max_historical_turns, 1)
|
|
296
|
-
history = tracker_as_readable_transcript(
|
|
297
|
-
|
|
298
|
-
|
|
323
|
+
history = tracker_as_readable_transcript(
|
|
324
|
+
tracker, max_turns=max_turns, turns_wrapper=turns_wrapper
|
|
325
|
+
)
|
|
299
326
|
|
|
300
327
|
prompt = Template(prompt_template_text).render(
|
|
301
328
|
history=history,
|
rasa/core/nlg/summarize.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
from
|
|
1
|
+
from itertools import groupby
|
|
2
|
+
from typing import Callable, List, Optional
|
|
2
3
|
|
|
3
4
|
import structlog
|
|
4
5
|
from jinja2 import Template
|
|
@@ -23,20 +24,49 @@ SUMMARY_PROMPT_TEMPLATE = Template(_DEFAULT_SUMMARIZER_TEMPLATE)
|
|
|
23
24
|
MAX_TURNS_DEFAULT = 20
|
|
24
25
|
|
|
25
26
|
|
|
27
|
+
def _count_multiple_utterances_as_single_turn(transcript: List[str]) -> List[str]:
|
|
28
|
+
"""Counts multiple utterances as a single turn.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
transcript: the lines of the transcript
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
transcript: with multiple utterances counted as a single turn
|
|
35
|
+
"""
|
|
36
|
+
if not transcript:
|
|
37
|
+
return []
|
|
38
|
+
|
|
39
|
+
def get_speaker_label(line: str) -> str:
|
|
40
|
+
return line.partition(": ")[0] if ": " in line else ""
|
|
41
|
+
|
|
42
|
+
modified_transcript = [
|
|
43
|
+
f"{speaker}: {' '.join(line.partition(': ')[2] for line in group)}"
|
|
44
|
+
for speaker, group in groupby(transcript, key=get_speaker_label)
|
|
45
|
+
if speaker
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
return modified_transcript
|
|
49
|
+
|
|
50
|
+
|
|
26
51
|
def _create_summarization_prompt(
|
|
27
|
-
tracker: DialogueStateTracker,
|
|
52
|
+
tracker: DialogueStateTracker,
|
|
53
|
+
max_turns: Optional[int],
|
|
54
|
+
turns_wrapper: Optional[Callable[[List[str]], List[str]]],
|
|
28
55
|
) -> str:
|
|
29
56
|
"""Creates an LLM prompt to summarize the conversation in the tracker.
|
|
30
57
|
|
|
31
58
|
Args:
|
|
32
59
|
tracker: tracker of the conversation to be summarized
|
|
33
60
|
max_turns: maximum number of turns to summarize
|
|
61
|
+
turns_wrapper: optional function to wrap the turns
|
|
34
62
|
|
|
35
63
|
|
|
36
64
|
Returns:
|
|
37
65
|
The prompt to summarize the conversation.
|
|
38
66
|
"""
|
|
39
|
-
transcript = tracker_as_readable_transcript(
|
|
67
|
+
transcript = tracker_as_readable_transcript(
|
|
68
|
+
tracker, max_turns=max_turns, turns_wrapper=turns_wrapper
|
|
69
|
+
)
|
|
40
70
|
return SUMMARY_PROMPT_TEMPLATE.render(
|
|
41
71
|
conversation=transcript,
|
|
42
72
|
)
|
|
@@ -46,6 +76,7 @@ async def summarize_conversation(
|
|
|
46
76
|
tracker: DialogueStateTracker,
|
|
47
77
|
llm: LLMClient,
|
|
48
78
|
max_turns: Optional[int] = MAX_TURNS_DEFAULT,
|
|
79
|
+
turns_wrapper: Optional[Callable[[List[str]], List[str]]] = None,
|
|
49
80
|
) -> str:
|
|
50
81
|
"""Summarizes the dialogue using the LLM.
|
|
51
82
|
|
|
@@ -53,11 +84,12 @@ async def summarize_conversation(
|
|
|
53
84
|
tracker: the tracker to summarize
|
|
54
85
|
llm: the LLM to use for summarization
|
|
55
86
|
max_turns: maximum number of turns to summarize
|
|
87
|
+
turns_wrapper: optional function to wrap the turns
|
|
56
88
|
|
|
57
89
|
Returns:
|
|
58
90
|
The summary of the dialogue.
|
|
59
91
|
"""
|
|
60
|
-
prompt = _create_summarization_prompt(tracker, max_turns)
|
|
92
|
+
prompt = _create_summarization_prompt(tracker, max_turns, turns_wrapper)
|
|
61
93
|
try:
|
|
62
94
|
llm_response = await llm.acompletion(prompt)
|
|
63
95
|
summarization = llm_response.choices[0].strip()
|
|
@@ -66,6 +98,8 @@ async def summarize_conversation(
|
|
|
66
98
|
)
|
|
67
99
|
return summarization
|
|
68
100
|
except Exception as e:
|
|
69
|
-
transcript = tracker_as_readable_transcript(
|
|
101
|
+
transcript = tracker_as_readable_transcript(
|
|
102
|
+
tracker, max_turns=max_turns, turns_wrapper=turns_wrapper
|
|
103
|
+
)
|
|
70
104
|
structlogger.error("summarization.error", error=e)
|
|
71
105
|
return transcript
|
|
@@ -46,12 +46,15 @@ from rasa.graph_components.providers.forms_provider import Forms
|
|
|
46
46
|
from rasa.graph_components.providers.responses_provider import Responses
|
|
47
47
|
from rasa.shared.constants import (
|
|
48
48
|
EMBEDDINGS_CONFIG_KEY,
|
|
49
|
+
MAX_COMPLETION_TOKENS_CONFIG_KEY,
|
|
50
|
+
MAX_RETRIES_CONFIG_KEY,
|
|
49
51
|
MODEL_CONFIG_KEY,
|
|
50
52
|
MODEL_GROUP_ID_CONFIG_KEY,
|
|
51
53
|
MODEL_NAME_CONFIG_KEY,
|
|
52
54
|
OPENAI_PROVIDER,
|
|
53
55
|
PROMPT_CONFIG_KEY,
|
|
54
56
|
PROVIDER_CONFIG_KEY,
|
|
57
|
+
TEMPERATURE_CONFIG_KEY,
|
|
55
58
|
TIMEOUT_CONFIG_KEY,
|
|
56
59
|
)
|
|
57
60
|
from rasa.shared.core.constants import (
|
|
@@ -135,14 +138,14 @@ DEFAULT_LLM_CONFIG = {
|
|
|
135
138
|
PROVIDER_CONFIG_KEY: OPENAI_PROVIDER,
|
|
136
139
|
MODEL_CONFIG_KEY: DEFAULT_OPENAI_CHAT_MODEL_NAME,
|
|
137
140
|
TIMEOUT_CONFIG_KEY: 10,
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
+
TEMPERATURE_CONFIG_KEY: 0.0,
|
|
142
|
+
MAX_COMPLETION_TOKENS_CONFIG_KEY: 256,
|
|
143
|
+
MAX_RETRIES_CONFIG_KEY: 1,
|
|
141
144
|
}
|
|
142
145
|
|
|
143
146
|
DEFAULT_EMBEDDINGS_CONFIG = {
|
|
144
147
|
PROVIDER_CONFIG_KEY: OPENAI_PROVIDER,
|
|
145
|
-
|
|
148
|
+
MODEL_CONFIG_KEY: DEFAULT_OPENAI_EMBEDDING_MODEL_NAME,
|
|
146
149
|
}
|
|
147
150
|
|
|
148
151
|
ENTERPRISE_SEARCH_PROMPT_FILE_NAME = "enterprise_search_policy_prompt.jinja2"
|
|
@@ -31,12 +31,14 @@ from rasa.graph_components.providers.responses_provider import Responses
|
|
|
31
31
|
from rasa.shared.constants import (
|
|
32
32
|
EMBEDDINGS_CONFIG_KEY,
|
|
33
33
|
LLM_CONFIG_KEY,
|
|
34
|
+
MAX_COMPLETION_TOKENS_CONFIG_KEY,
|
|
34
35
|
MODEL_CONFIG_KEY,
|
|
35
36
|
MODEL_GROUP_ID_CONFIG_KEY,
|
|
36
37
|
MODEL_NAME_CONFIG_KEY,
|
|
37
38
|
OPENAI_PROVIDER,
|
|
38
39
|
PROMPT_CONFIG_KEY,
|
|
39
40
|
PROVIDER_CONFIG_KEY,
|
|
41
|
+
TEMPERATURE_CONFIG_KEY,
|
|
40
42
|
TIMEOUT_CONFIG_KEY,
|
|
41
43
|
)
|
|
42
44
|
from rasa.shared.core.constants import ACTION_LISTEN_NAME
|
|
@@ -111,14 +113,14 @@ NLU_ABSTENTION_THRESHOLD = "nlu_abstention_threshold"
|
|
|
111
113
|
DEFAULT_LLM_CONFIG = {
|
|
112
114
|
PROVIDER_CONFIG_KEY: OPENAI_PROVIDER,
|
|
113
115
|
MODEL_CONFIG_KEY: DEFAULT_OPENAI_CHAT_MODEL_NAME,
|
|
114
|
-
|
|
115
|
-
|
|
116
|
+
TEMPERATURE_CONFIG_KEY: 0.0,
|
|
117
|
+
MAX_COMPLETION_TOKENS_CONFIG_KEY: DEFAULT_OPENAI_MAX_GENERATED_TOKENS,
|
|
116
118
|
TIMEOUT_CONFIG_KEY: 5,
|
|
117
119
|
}
|
|
118
120
|
|
|
119
121
|
DEFAULT_EMBEDDINGS_CONFIG = {
|
|
120
122
|
PROVIDER_CONFIG_KEY: OPENAI_PROVIDER,
|
|
121
|
-
|
|
123
|
+
MODEL_CONFIG_KEY: DEFAULT_OPENAI_EMBEDDING_MODEL_NAME,
|
|
122
124
|
}
|
|
123
125
|
|
|
124
126
|
DEFAULT_INTENTLESS_PROMPT_TEMPLATE = importlib.resources.open_text(
|
|
@@ -344,8 +346,6 @@ class IntentlessPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Policy):
|
|
|
344
346
|
# ensures that the policy will not override a deterministic policy
|
|
345
347
|
# which utilizes the nlu predictions confidence (e.g. Memoization).
|
|
346
348
|
NLU_ABSTENTION_THRESHOLD: 0.9,
|
|
347
|
-
LLM_CONFIG_KEY: DEFAULT_LLM_CONFIG,
|
|
348
|
-
EMBEDDINGS_CONFIG_KEY: DEFAULT_EMBEDDINGS_CONFIG,
|
|
349
349
|
PROMPT_CONFIG_KEY: DEFAULT_INTENTLESS_PROMPT_TEMPLATE,
|
|
350
350
|
}
|
|
351
351
|
|
|
@@ -381,13 +381,19 @@ class IntentlessPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Policy):
|
|
|
381
381
|
super().__init__(config, model_storage, resource, execution_context, featurizer)
|
|
382
382
|
|
|
383
383
|
# Resolve LLM config
|
|
384
|
-
self.config[LLM_CONFIG_KEY] =
|
|
385
|
-
|
|
384
|
+
self.config[LLM_CONFIG_KEY] = combine_custom_and_default_config(
|
|
385
|
+
resolve_model_client_config(
|
|
386
|
+
self.config.get(LLM_CONFIG_KEY), IntentlessPolicy.__name__
|
|
387
|
+
),
|
|
388
|
+
DEFAULT_LLM_CONFIG,
|
|
386
389
|
)
|
|
387
390
|
|
|
388
391
|
# Resolve embeddings config
|
|
389
|
-
self.config[EMBEDDINGS_CONFIG_KEY] =
|
|
390
|
-
|
|
392
|
+
self.config[EMBEDDINGS_CONFIG_KEY] = combine_custom_and_default_config(
|
|
393
|
+
resolve_model_client_config(
|
|
394
|
+
self.config.get(EMBEDDINGS_CONFIG_KEY), IntentlessPolicy.__name__
|
|
395
|
+
),
|
|
396
|
+
DEFAULT_EMBEDDINGS_CONFIG,
|
|
391
397
|
)
|
|
392
398
|
|
|
393
399
|
self.nlu_abstention_threshold: float = self.config[NLU_ABSTENTION_THRESHOLD]
|
rasa/core/utils.py
CHANGED
|
@@ -244,6 +244,10 @@ class AvailableEndpoints:
|
|
|
244
244
|
cls._instance = cls.read_endpoints(endpoint_file)
|
|
245
245
|
return cls._instance
|
|
246
246
|
|
|
247
|
+
@classmethod
|
|
248
|
+
def reset_instance(cls) -> None:
|
|
249
|
+
cls._instance = None
|
|
250
|
+
|
|
247
251
|
|
|
248
252
|
def read_endpoints_from_path(
|
|
249
253
|
endpoints_path: Optional[Union[Path, Text]] = None,
|
|
@@ -23,11 +23,14 @@ from rasa.engine.recipes.default_recipe import DefaultV1Recipe
|
|
|
23
23
|
from rasa.engine.storage.resource import Resource
|
|
24
24
|
from rasa.engine.storage.storage import ModelStorage
|
|
25
25
|
from rasa.shared.constants import (
|
|
26
|
+
LOGIT_BIAS_CONFIG_KEY,
|
|
27
|
+
MAX_COMPLETION_TOKENS_CONFIG_KEY,
|
|
26
28
|
MODEL_CONFIG_KEY,
|
|
27
29
|
OPENAI_PROVIDER,
|
|
28
30
|
PROMPT_CONFIG_KEY,
|
|
29
31
|
PROVIDER_CONFIG_KEY,
|
|
30
32
|
ROUTE_TO_CALM_SLOT,
|
|
33
|
+
TEMPERATURE_CONFIG_KEY,
|
|
31
34
|
TIMEOUT_CONFIG_KEY,
|
|
32
35
|
)
|
|
33
36
|
from rasa.shared.core.trackers import DialogueStateTracker
|
|
@@ -66,9 +69,11 @@ DEFAULT_LLM_CONFIG = {
|
|
|
66
69
|
PROVIDER_CONFIG_KEY: OPENAI_PROVIDER,
|
|
67
70
|
MODEL_CONFIG_KEY: DEFAULT_OPENAI_CHAT_MODEL_NAME,
|
|
68
71
|
TIMEOUT_CONFIG_KEY: 7,
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
+
TEMPERATURE_CONFIG_KEY: 0.0,
|
|
73
|
+
MAX_COMPLETION_TOKENS_CONFIG_KEY: 1,
|
|
74
|
+
LOGIT_BIAS_CONFIG_KEY: {
|
|
75
|
+
str(token_id): 100 for token_id in A_TO_C_TOKEN_IDS_CHATGPT
|
|
76
|
+
},
|
|
72
77
|
}
|
|
73
78
|
|
|
74
79
|
structlogger = structlog.get_logger()
|
|
@@ -119,7 +119,7 @@ class ClarifyCommand(Command):
|
|
|
119
119
|
mapper = {
|
|
120
120
|
CommandSyntaxVersion.v1: r"Clarify\(([\"\'a-zA-Z0-9_, -]*)\)",
|
|
121
121
|
CommandSyntaxVersion.v2: (
|
|
122
|
-
r"""^[\s\W\d]*disambiguate flows (["'a-zA-Z0-9_, -]*)[
|
|
122
|
+
r"""^[\s\W\d]*disambiguate flows (["'a-zA-Z0-9_, -]*)[\W\\n]*$"""
|
|
123
123
|
),
|
|
124
124
|
}
|
|
125
125
|
return mapper.get(
|
|
@@ -190,7 +190,7 @@ class SetSlotCommand(Command):
|
|
|
190
190
|
r"""SetSlot\(['"]?([a-zA-Z_][a-zA-Z0-9_-]*)['"]?, ?['"]?(.*)['"]?\)"""
|
|
191
191
|
),
|
|
192
192
|
CommandSyntaxVersion.v2: (
|
|
193
|
-
r"""^[\s\W\d]*set slot ['"`]?([a-zA-Z_][a-zA-Z0-9_-]*)['"`]? ['"`]?(.+?)[
|
|
193
|
+
r"""^[\s\W\d]*set slot ['"`]?([a-zA-Z_][a-zA-Z0-9_-]*)['"`]? ['"`]?(.+?)[\W\\n]*$""" # noqa: E501
|
|
194
194
|
),
|
|
195
195
|
}
|
|
196
196
|
return mapper.get(
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from rasa.shared.constants import (
|
|
2
|
-
|
|
2
|
+
MAX_COMPLETION_TOKENS_CONFIG_KEY,
|
|
3
3
|
MODEL_CONFIG_KEY,
|
|
4
4
|
OPENAI_PROVIDER,
|
|
5
5
|
PROVIDER_CONFIG_KEY,
|
|
@@ -15,7 +15,7 @@ DEFAULT_LLM_CONFIG = {
|
|
|
15
15
|
PROVIDER_CONFIG_KEY: OPENAI_PROVIDER,
|
|
16
16
|
MODEL_CONFIG_KEY: DEFAULT_OPENAI_CHAT_MODEL_NAME_ADVANCED,
|
|
17
17
|
TEMPERATURE_CONFIG_KEY: 0.0,
|
|
18
|
-
|
|
18
|
+
MAX_COMPLETION_TOKENS_CONFIG_KEY: DEFAULT_OPENAI_MAX_GENERATED_TOKENS,
|
|
19
19
|
TIMEOUT_CONFIG_KEY: 7,
|
|
20
20
|
}
|
|
21
21
|
|
|
@@ -47,7 +47,7 @@ from rasa.shared.constants import (
|
|
|
47
47
|
AWS_BEDROCK_PROVIDER,
|
|
48
48
|
AZURE_OPENAI_PROVIDER,
|
|
49
49
|
EMBEDDINGS_CONFIG_KEY,
|
|
50
|
-
|
|
50
|
+
MAX_COMPLETION_TOKENS_CONFIG_KEY,
|
|
51
51
|
PROMPT_TEMPLATE_CONFIG_KEY,
|
|
52
52
|
ROUTE_TO_CALM_SLOT,
|
|
53
53
|
TEMPERATURE_CONFIG_KEY,
|
|
@@ -81,7 +81,7 @@ DEFAULT_LLM_CONFIG = {
|
|
|
81
81
|
PROVIDER_CONFIG_KEY: OPENAI_PROVIDER,
|
|
82
82
|
MODEL_CONFIG_KEY: MODEL_NAME_GPT_4O_2024_11_20,
|
|
83
83
|
TEMPERATURE_CONFIG_KEY: 0.0,
|
|
84
|
-
|
|
84
|
+
MAX_COMPLETION_TOKENS_CONFIG_KEY: DEFAULT_OPENAI_MAX_GENERATED_TOKENS,
|
|
85
85
|
TIMEOUT_CONFIG_KEY: 7,
|
|
86
86
|
}
|
|
87
87
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from typing import Any, Dict, List, Text
|
|
3
|
+
from typing import Any, Dict, List, Optional, Text
|
|
4
4
|
|
|
5
5
|
import rasa.dialogue_understanding.processor.command_processor
|
|
6
6
|
from rasa.engine.graph import ExecutionContext, GraphComponent
|
|
@@ -37,8 +37,8 @@ class CommandProcessorComponent(GraphComponent):
|
|
|
37
37
|
self,
|
|
38
38
|
tracker: DialogueStateTracker,
|
|
39
39
|
flows: FlowsList,
|
|
40
|
-
story_graph: StoryGraph,
|
|
41
40
|
domain: Domain,
|
|
41
|
+
story_graph: Optional[StoryGraph] = None,
|
|
42
42
|
) -> List[Event]:
|
|
43
43
|
"""Execute commands to update tracker state."""
|
|
44
44
|
return rasa.dialogue_understanding.processor.command_processor.execute_commands(
|
|
@@ -33,6 +33,7 @@ from rasa.e2e_test.e2e_test_runner import E2ETestRunner
|
|
|
33
33
|
from rasa.shared.core.events import UserUttered
|
|
34
34
|
from rasa.shared.core.trackers import DialogueStateTracker
|
|
35
35
|
from rasa.shared.nlu.constants import PREDICTED_COMMANDS, PROMPTS
|
|
36
|
+
from rasa.shared.utils.llm import create_tracker_for_user_step
|
|
36
37
|
from rasa.utils.endpoints import EndpointConfig
|
|
37
38
|
|
|
38
39
|
structlogger = structlog.get_logger()
|
|
@@ -178,8 +179,9 @@ class DialogueUnderstandingTestRunner:
|
|
|
178
179
|
# create and save the tracker at the time just
|
|
179
180
|
# before the user message was sent
|
|
180
181
|
step_sender_id = f"{sender_id}_{user_step_index}"
|
|
181
|
-
await
|
|
182
|
+
await create_tracker_for_user_step(
|
|
182
183
|
step_sender_id,
|
|
184
|
+
self.agent,
|
|
183
185
|
test_case_tracker,
|
|
184
186
|
user_uttered_event_indices[user_step_index],
|
|
185
187
|
)
|
|
@@ -280,26 +282,6 @@ class DialogueUnderstandingTestRunner:
|
|
|
280
282
|
|
|
281
283
|
return user_uttered_event
|
|
282
284
|
|
|
283
|
-
async def _create_tracker_for_user_step(
|
|
284
|
-
self,
|
|
285
|
-
step_sender_id: str,
|
|
286
|
-
test_case_tracker: DialogueStateTracker,
|
|
287
|
-
index_user_uttered_event: int,
|
|
288
|
-
) -> None:
|
|
289
|
-
"""Creates a tracker for the user step."""
|
|
290
|
-
tracker = test_case_tracker.copy()
|
|
291
|
-
# modify the sender id so that the test case tracker is not overwritten
|
|
292
|
-
tracker.sender_id = step_sender_id
|
|
293
|
-
|
|
294
|
-
if tracker.events:
|
|
295
|
-
# get timestamp of the event just before the user uttered event
|
|
296
|
-
timestamp = tracker.events[index_user_uttered_event - 1].timestamp
|
|
297
|
-
# revert the tracker to the event just before the user uttered event
|
|
298
|
-
tracker = tracker.travel_back_in_time(timestamp)
|
|
299
|
-
|
|
300
|
-
# store the tracker with the unique sender id
|
|
301
|
-
await self.agent.tracker_store.save(tracker)
|
|
302
|
-
|
|
303
285
|
async def _send_user_message(
|
|
304
286
|
self,
|
|
305
287
|
sender_id: str,
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
from datetime import datetime
|
|
2
1
|
from typing import List, Optional
|
|
3
2
|
|
|
4
3
|
import structlog
|
|
@@ -24,6 +23,7 @@ from rasa.shared.core.constants import SlotMappingType
|
|
|
24
23
|
from rasa.shared.core.events import BotUttered, SlotSet, UserUttered
|
|
25
24
|
from rasa.shared.core.trackers import DialogueStateTracker
|
|
26
25
|
from rasa.shared.nlu.constants import COMMANDS, ENTITIES, INTENT
|
|
26
|
+
from rasa.shared.utils.llm import generate_sender_id
|
|
27
27
|
|
|
28
28
|
structlogger = structlog.get_logger()
|
|
29
29
|
|
|
@@ -52,7 +52,7 @@ class TestCaseTrackerSimulator:
|
|
|
52
52
|
self.test_case = test_case
|
|
53
53
|
self.output_channel = output_channel or CollectingOutputChannel()
|
|
54
54
|
|
|
55
|
-
self.sender_id = self.
|
|
55
|
+
self.sender_id = generate_sender_id(self.test_case.name)
|
|
56
56
|
|
|
57
57
|
async def simulate_test_case(
|
|
58
58
|
self,
|
|
@@ -150,10 +150,6 @@ class TestCaseTrackerSimulator:
|
|
|
150
150
|
user_uttered_event_indices=user_uttered_event_indices,
|
|
151
151
|
)
|
|
152
152
|
|
|
153
|
-
def _generate_sender_id(self) -> str:
|
|
154
|
-
# add timestamp suffix to ensure sender_id is unique
|
|
155
|
-
return f"{self.test_case.name}_{datetime.now()}"
|
|
156
|
-
|
|
157
153
|
@staticmethod
|
|
158
154
|
async def _get_latest_user_uttered_event_index(
|
|
159
155
|
tracker: DialogueStateTracker, user_uttered_event_indices: List[int]
|
|
@@ -396,7 +396,9 @@ class DefaultV1Recipe(Recipe):
|
|
|
396
396
|
return preprocessors
|
|
397
397
|
|
|
398
398
|
def _get_needs_from_args(
|
|
399
|
-
self,
|
|
399
|
+
self,
|
|
400
|
+
component: Type[GraphComponent],
|
|
401
|
+
fn_name: str,
|
|
400
402
|
) -> Dict[str, str]:
|
|
401
403
|
"""Get the needed arguments from the method on the component.
|
|
402
404
|
|
|
@@ -434,6 +436,7 @@ class DefaultV1Recipe(Recipe):
|
|
|
434
436
|
parameters = {
|
|
435
437
|
name
|
|
436
438
|
for name, param in sig.parameters.items()
|
|
439
|
+
# only consider parameters which are positional or keyword
|
|
437
440
|
if param.kind == param.POSITIONAL_OR_KEYWORD
|
|
438
441
|
}
|
|
439
442
|
|
|
@@ -752,8 +755,28 @@ class DefaultV1Recipe(Recipe):
|
|
|
752
755
|
predict_config, predict_nodes, train_nodes, preprocessors
|
|
753
756
|
)
|
|
754
757
|
|
|
758
|
+
# The `story_graph_provider` is only needed if the intentless policy is used.
|
|
759
|
+
# If it is not used, we can remove it from the nodes as it slows down the
|
|
760
|
+
# loading time if users have a large number of stories.
|
|
761
|
+
if not self._intentless_policy_used(predict_nodes):
|
|
762
|
+
# Removes the `story_graph_provider` from the nodes
|
|
763
|
+
predict_nodes.pop("story_graph_provider", None)
|
|
764
|
+
if "command_processor" in predict_nodes:
|
|
765
|
+
# Removes story_graph from the command processor inputs
|
|
766
|
+
predict_nodes["command_processor"].needs.pop("story_graph", None)
|
|
767
|
+
|
|
755
768
|
return predict_nodes
|
|
756
769
|
|
|
770
|
+
@staticmethod
|
|
771
|
+
def _intentless_policy_used(nodes: Dict[Text, SchemaNode]) -> bool:
|
|
772
|
+
"""Checks if the intentless policy is used in the nodes."""
|
|
773
|
+
from rasa.core import IntentlessPolicy
|
|
774
|
+
|
|
775
|
+
for schema_node in nodes.values():
|
|
776
|
+
if schema_node.matches_type(IntentlessPolicy):
|
|
777
|
+
return True
|
|
778
|
+
return False
|
|
779
|
+
|
|
757
780
|
def _add_nlu_predict_nodes(
|
|
758
781
|
self,
|
|
759
782
|
last_run_node: Text,
|
|
@@ -924,7 +947,8 @@ class DefaultV1Recipe(Recipe):
|
|
|
924
947
|
predict_nodes["command_processor"] = SchemaNode(
|
|
925
948
|
**DEFAULT_PREDICT_KWARGS,
|
|
926
949
|
needs=self._get_needs_from_args(
|
|
927
|
-
CommandProcessorComponent,
|
|
950
|
+
CommandProcessorComponent,
|
|
951
|
+
"execute_commands",
|
|
928
952
|
),
|
|
929
953
|
uses=CommandProcessorComponent,
|
|
930
954
|
fn="execute_commands",
|