rasa-pro 3.9.18__py3-none-any.whl → 3.10.16__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.
- README.md +0 -374
- rasa/__init__.py +1 -2
- rasa/__main__.py +5 -0
- rasa/anonymization/anonymization_rule_executor.py +2 -2
- rasa/api.py +27 -23
- rasa/cli/arguments/data.py +27 -2
- rasa/cli/arguments/default_arguments.py +25 -3
- rasa/cli/arguments/run.py +9 -9
- rasa/cli/arguments/train.py +11 -3
- rasa/cli/data.py +70 -8
- rasa/cli/e2e_test.py +104 -431
- rasa/cli/evaluate.py +1 -1
- rasa/cli/interactive.py +1 -0
- rasa/cli/llm_fine_tuning.py +398 -0
- rasa/cli/project_templates/calm/endpoints.yml +1 -1
- rasa/cli/project_templates/tutorial/endpoints.yml +1 -1
- rasa/cli/run.py +15 -14
- rasa/cli/scaffold.py +10 -8
- rasa/cli/studio/studio.py +35 -5
- rasa/cli/train.py +56 -8
- rasa/cli/utils.py +22 -5
- rasa/cli/x.py +1 -1
- rasa/constants.py +7 -1
- rasa/core/actions/action.py +98 -49
- rasa/core/actions/action_run_slot_rejections.py +4 -1
- rasa/core/actions/custom_action_executor.py +9 -6
- rasa/core/actions/direct_custom_actions_executor.py +80 -0
- rasa/core/actions/e2e_stub_custom_action_executor.py +68 -0
- rasa/core/actions/grpc_custom_action_executor.py +2 -2
- rasa/core/actions/http_custom_action_executor.py +6 -5
- rasa/core/agent.py +21 -17
- rasa/core/channels/__init__.py +2 -0
- rasa/core/channels/audiocodes.py +1 -16
- rasa/core/channels/voice_aware/__init__.py +0 -0
- rasa/core/channels/voice_aware/jambonz.py +103 -0
- rasa/core/channels/voice_aware/jambonz_protocol.py +344 -0
- rasa/core/channels/voice_aware/utils.py +20 -0
- rasa/core/channels/voice_native/__init__.py +0 -0
- rasa/core/constants.py +6 -1
- rasa/core/information_retrieval/faiss.py +7 -4
- rasa/core/information_retrieval/information_retrieval.py +8 -0
- rasa/core/information_retrieval/milvus.py +9 -2
- rasa/core/information_retrieval/qdrant.py +1 -1
- rasa/core/nlg/contextual_response_rephraser.py +32 -10
- rasa/core/nlg/summarize.py +4 -3
- rasa/core/policies/enterprise_search_policy.py +113 -45
- rasa/core/policies/flows/flow_executor.py +122 -76
- rasa/core/policies/intentless_policy.py +83 -29
- rasa/core/processor.py +72 -54
- rasa/core/run.py +5 -4
- rasa/core/tracker_store.py +8 -4
- rasa/core/training/interactive.py +1 -1
- rasa/core/utils.py +56 -57
- rasa/dialogue_understanding/coexistence/llm_based_router.py +53 -13
- rasa/dialogue_understanding/commands/__init__.py +6 -0
- rasa/dialogue_understanding/commands/restart_command.py +58 -0
- rasa/dialogue_understanding/commands/session_start_command.py +59 -0
- rasa/dialogue_understanding/commands/utils.py +40 -0
- rasa/dialogue_understanding/generator/constants.py +10 -3
- rasa/dialogue_understanding/generator/flow_retrieval.py +21 -5
- rasa/dialogue_understanding/generator/llm_based_command_generator.py +13 -3
- rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +134 -90
- rasa/dialogue_understanding/generator/nlu_command_adapter.py +47 -7
- rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +127 -41
- rasa/dialogue_understanding/patterns/restart.py +37 -0
- rasa/dialogue_understanding/patterns/session_start.py +37 -0
- rasa/dialogue_understanding/processor/command_processor.py +16 -3
- rasa/dialogue_understanding/processor/command_processor_component.py +6 -2
- rasa/e2e_test/aggregate_test_stats_calculator.py +134 -0
- rasa/e2e_test/assertions.py +1223 -0
- rasa/e2e_test/assertions_schema.yml +106 -0
- rasa/e2e_test/constants.py +20 -0
- rasa/e2e_test/e2e_config.py +220 -0
- rasa/e2e_test/e2e_config_schema.yml +26 -0
- rasa/e2e_test/e2e_test_case.py +131 -8
- rasa/e2e_test/e2e_test_converter.py +363 -0
- rasa/e2e_test/e2e_test_converter_prompt.jinja2 +70 -0
- rasa/e2e_test/e2e_test_coverage_report.py +364 -0
- rasa/e2e_test/e2e_test_result.py +26 -6
- rasa/e2e_test/e2e_test_runner.py +493 -71
- rasa/e2e_test/e2e_test_schema.yml +96 -0
- rasa/e2e_test/pykwalify_extensions.py +39 -0
- rasa/e2e_test/stub_custom_action.py +70 -0
- rasa/e2e_test/utils/__init__.py +0 -0
- rasa/e2e_test/utils/e2e_yaml_utils.py +55 -0
- rasa/e2e_test/utils/io.py +598 -0
- rasa/e2e_test/utils/validation.py +80 -0
- rasa/engine/graph.py +9 -3
- rasa/engine/recipes/default_components.py +0 -2
- rasa/engine/recipes/default_recipe.py +10 -2
- rasa/engine/storage/local_model_storage.py +40 -12
- rasa/engine/validation.py +78 -1
- rasa/env.py +9 -0
- rasa/graph_components/providers/story_graph_provider.py +59 -6
- rasa/llm_fine_tuning/__init__.py +0 -0
- rasa/llm_fine_tuning/annotation_module.py +241 -0
- rasa/llm_fine_tuning/conversations.py +144 -0
- rasa/llm_fine_tuning/llm_data_preparation_module.py +178 -0
- rasa/llm_fine_tuning/notebooks/unsloth_finetuning.ipynb +407 -0
- rasa/llm_fine_tuning/paraphrasing/__init__.py +0 -0
- rasa/llm_fine_tuning/paraphrasing/conversation_rephraser.py +281 -0
- rasa/llm_fine_tuning/paraphrasing/default_rephrase_prompt_template.jina2 +44 -0
- rasa/llm_fine_tuning/paraphrasing/rephrase_validator.py +121 -0
- rasa/llm_fine_tuning/paraphrasing/rephrased_user_message.py +10 -0
- rasa/llm_fine_tuning/paraphrasing_module.py +128 -0
- rasa/llm_fine_tuning/storage.py +174 -0
- rasa/llm_fine_tuning/train_test_split_module.py +441 -0
- rasa/model_training.py +56 -16
- rasa/nlu/persistor.py +157 -36
- rasa/server.py +45 -10
- rasa/shared/constants.py +76 -16
- rasa/shared/core/domain.py +27 -19
- rasa/shared/core/events.py +28 -2
- rasa/shared/core/flows/flow.py +208 -13
- rasa/shared/core/flows/flow_path.py +84 -0
- rasa/shared/core/flows/flows_list.py +33 -11
- rasa/shared/core/flows/flows_yaml_schema.json +269 -193
- rasa/shared/core/flows/validation.py +112 -25
- rasa/shared/core/flows/yaml_flows_io.py +149 -10
- rasa/shared/core/trackers.py +6 -0
- rasa/shared/core/training_data/structures.py +20 -0
- rasa/shared/core/training_data/visualization.html +2 -2
- rasa/shared/exceptions.py +4 -0
- rasa/shared/importers/importer.py +64 -16
- rasa/shared/nlu/constants.py +2 -0
- rasa/shared/providers/_configs/__init__.py +0 -0
- rasa/shared/providers/_configs/azure_openai_client_config.py +183 -0
- rasa/shared/providers/_configs/client_config.py +57 -0
- rasa/shared/providers/_configs/default_litellm_client_config.py +130 -0
- rasa/shared/providers/_configs/huggingface_local_embedding_client_config.py +234 -0
- rasa/shared/providers/_configs/openai_client_config.py +175 -0
- rasa/shared/providers/_configs/self_hosted_llm_client_config.py +176 -0
- rasa/shared/providers/_configs/utils.py +101 -0
- rasa/shared/providers/_ssl_verification_utils.py +124 -0
- rasa/shared/providers/embedding/__init__.py +0 -0
- rasa/shared/providers/embedding/_base_litellm_embedding_client.py +259 -0
- rasa/shared/providers/embedding/_langchain_embedding_client_adapter.py +74 -0
- rasa/shared/providers/embedding/azure_openai_embedding_client.py +277 -0
- rasa/shared/providers/embedding/default_litellm_embedding_client.py +102 -0
- rasa/shared/providers/embedding/embedding_client.py +90 -0
- rasa/shared/providers/embedding/embedding_response.py +41 -0
- rasa/shared/providers/embedding/huggingface_local_embedding_client.py +191 -0
- rasa/shared/providers/embedding/openai_embedding_client.py +172 -0
- rasa/shared/providers/llm/__init__.py +0 -0
- rasa/shared/providers/llm/_base_litellm_client.py +251 -0
- rasa/shared/providers/llm/azure_openai_llm_client.py +338 -0
- rasa/shared/providers/llm/default_litellm_llm_client.py +84 -0
- rasa/shared/providers/llm/llm_client.py +76 -0
- rasa/shared/providers/llm/llm_response.py +50 -0
- rasa/shared/providers/llm/openai_llm_client.py +155 -0
- rasa/shared/providers/llm/self_hosted_llm_client.py +293 -0
- rasa/shared/providers/mappings.py +75 -0
- rasa/shared/utils/cli.py +30 -0
- rasa/shared/utils/io.py +65 -2
- rasa/shared/utils/llm.py +246 -200
- rasa/shared/utils/yaml.py +121 -15
- rasa/studio/auth.py +6 -4
- rasa/studio/config.py +13 -4
- rasa/studio/constants.py +1 -0
- rasa/studio/data_handler.py +10 -3
- rasa/studio/download.py +19 -13
- rasa/studio/train.py +2 -3
- rasa/studio/upload.py +19 -11
- rasa/telemetry.py +113 -58
- rasa/tracing/instrumentation/attribute_extractors.py +32 -17
- rasa/utils/common.py +18 -19
- rasa/utils/endpoints.py +7 -4
- rasa/utils/json_utils.py +60 -0
- rasa/utils/licensing.py +9 -1
- rasa/utils/ml_utils.py +4 -2
- rasa/validator.py +213 -3
- rasa/version.py +1 -1
- rasa_pro-3.10.16.dist-info/METADATA +196 -0
- {rasa_pro-3.9.18.dist-info → rasa_pro-3.10.16.dist-info}/RECORD +179 -113
- rasa/nlu/classifiers/llm_intent_classifier.py +0 -519
- rasa/shared/providers/openai/clients.py +0 -43
- rasa/shared/providers/openai/session_handler.py +0 -110
- rasa_pro-3.9.18.dist-info/METADATA +0 -563
- /rasa/{shared/providers/openai → cli/project_templates/tutorial/actions}/__init__.py +0 -0
- /rasa/cli/project_templates/tutorial/{actions.py → actions/actions.py} +0 -0
- {rasa_pro-3.9.18.dist-info → rasa_pro-3.10.16.dist-info}/NOTICE +0 -0
- {rasa_pro-3.9.18.dist-info → rasa_pro-3.10.16.dist-info}/WHEEL +0 -0
- {rasa_pro-3.9.18.dist-info → rasa_pro-3.10.16.dist-info}/entry_points.txt +0 -0
rasa/shared/utils/yaml.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import datetime
|
|
1
2
|
import logging
|
|
2
3
|
import os
|
|
3
4
|
import re
|
|
@@ -12,15 +13,17 @@ from typing import Dict, List, Optional, Any, Callable, Tuple, Union
|
|
|
12
13
|
import jsonschema
|
|
13
14
|
from importlib_resources import files
|
|
14
15
|
from packaging import version
|
|
15
|
-
from packaging.version import LegacyVersion
|
|
16
16
|
from pykwalify.core import Core
|
|
17
17
|
from pykwalify.errors import SchemaError
|
|
18
18
|
from ruamel import yaml as yaml
|
|
19
19
|
from ruamel.yaml import RoundTripRepresenter, YAMLError
|
|
20
20
|
from ruamel.yaml.constructor import DuplicateKeyError, BaseConstructor, ScalarNode
|
|
21
21
|
from ruamel.yaml.comments import CommentedSeq, CommentedMap
|
|
22
|
+
from ruamel.yaml.loader import SafeLoader
|
|
22
23
|
|
|
23
24
|
from rasa.shared.constants import (
|
|
25
|
+
ASSERTIONS_SCHEMA_EXTENSIONS_FILE,
|
|
26
|
+
ASSERTIONS_SCHEMA_FILE,
|
|
24
27
|
MODEL_CONFIG_SCHEMA_FILE,
|
|
25
28
|
CONFIG_SCHEMA_FILE,
|
|
26
29
|
DOCS_URL_TRAINING_DATA,
|
|
@@ -413,30 +416,108 @@ def validate_raw_yaml_using_schema_file_with_responses(
|
|
|
413
416
|
)
|
|
414
417
|
|
|
415
418
|
|
|
416
|
-
def read_yaml(
|
|
419
|
+
def read_yaml(
|
|
420
|
+
content: str,
|
|
421
|
+
reader_type: Union[str, List[str]] = "safe",
|
|
422
|
+
**kwargs: Any,
|
|
423
|
+
) -> Any:
|
|
417
424
|
"""Parses yaml from a text.
|
|
418
425
|
|
|
419
426
|
Args:
|
|
420
427
|
content: A text containing yaml content.
|
|
421
428
|
reader_type: Reader type to use. By default, "safe" will be used.
|
|
429
|
+
**kwargs: Any
|
|
422
430
|
|
|
423
431
|
Raises:
|
|
424
432
|
ruamel.yaml.parser.ParserError: If there was an error when parsing the YAML.
|
|
425
433
|
"""
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
+
custom_constructor = kwargs.get("custom_constructor", None)
|
|
435
|
+
|
|
436
|
+
# Create YAML parser with custom constructor
|
|
437
|
+
yaml_parser, reset_constructors = create_yaml_parser(
|
|
438
|
+
reader_type, custom_constructor
|
|
439
|
+
)
|
|
440
|
+
yaml_content = yaml_parser.load(content) or {}
|
|
441
|
+
|
|
442
|
+
# Reset to default constructors
|
|
443
|
+
reset_constructors()
|
|
444
|
+
|
|
445
|
+
return yaml_content
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
def create_yaml_parser(
|
|
449
|
+
reader_type: str,
|
|
450
|
+
custom_constructor: Optional[Callable] = None,
|
|
451
|
+
) -> Tuple[yaml.YAML, Callable[[], None]]:
|
|
452
|
+
"""Create a YAML parser with an optional custom constructor.
|
|
453
|
+
|
|
454
|
+
Args:
|
|
455
|
+
reader_type (str): The type of the reader
|
|
456
|
+
(e.g., 'safe', 'rt', 'unsafe').
|
|
457
|
+
custom_constructor (Optional[Callable]):
|
|
458
|
+
A custom constructor function for YAML parsing.
|
|
434
459
|
|
|
460
|
+
Returns:
|
|
461
|
+
Tuple[yaml.YAML, Callable[[], None]]: A tuple containing
|
|
462
|
+
the YAML parser and a function to reset constructors to
|
|
463
|
+
their original state.
|
|
464
|
+
"""
|
|
435
465
|
yaml_parser = yaml.YAML(typ=reader_type)
|
|
436
466
|
yaml_parser.version = YAML_VERSION # type: ignore[assignment]
|
|
437
467
|
yaml_parser.preserve_quotes = True # type: ignore[assignment]
|
|
438
468
|
|
|
439
|
-
|
|
469
|
+
# Save the original constructors
|
|
470
|
+
original_mapping_constructor = yaml_parser.constructor.yaml_constructors.get(
|
|
471
|
+
yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG
|
|
472
|
+
)
|
|
473
|
+
original_sequence_constructor = yaml_parser.constructor.yaml_constructors.get(
|
|
474
|
+
yaml.resolver.BaseResolver.DEFAULT_SEQUENCE_TAG
|
|
475
|
+
)
|
|
476
|
+
|
|
477
|
+
if custom_constructor is not None:
|
|
478
|
+
# Attach the custom constructor to the loader
|
|
479
|
+
yaml_parser.constructor.add_constructor(
|
|
480
|
+
yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, custom_constructor
|
|
481
|
+
)
|
|
482
|
+
yaml_parser.constructor.add_constructor(
|
|
483
|
+
yaml.resolver.BaseResolver.DEFAULT_SEQUENCE_TAG, custom_constructor
|
|
484
|
+
)
|
|
485
|
+
|
|
486
|
+
def reset_constructors() -> None:
|
|
487
|
+
"""Reset the constructors back to their original state."""
|
|
488
|
+
yaml_parser.constructor.add_constructor(
|
|
489
|
+
yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, original_mapping_constructor
|
|
490
|
+
)
|
|
491
|
+
yaml_parser.constructor.add_constructor(
|
|
492
|
+
yaml.resolver.BaseResolver.DEFAULT_SEQUENCE_TAG,
|
|
493
|
+
original_sequence_constructor,
|
|
494
|
+
)
|
|
495
|
+
|
|
496
|
+
def custom_date_constructor(loader: SafeLoader, node: ScalarNode) -> str:
|
|
497
|
+
"""Custom constructor for parsing dates in the format '%Y-%m-%d'.
|
|
498
|
+
|
|
499
|
+
This constructor parses dates in the '%Y-%m-%d' format and returns them as
|
|
500
|
+
strings instead of datetime objects. This change was introduced because the
|
|
501
|
+
default timestamp constructor in ruamel.yaml returns datetime objects, which
|
|
502
|
+
caused issues in our use case where the `api_version` in the LLM config must
|
|
503
|
+
be a string, but was being interpreted as a datetime object.
|
|
504
|
+
"""
|
|
505
|
+
value = loader.construct_scalar(node)
|
|
506
|
+
try:
|
|
507
|
+
# Attempt to parse the date
|
|
508
|
+
date_obj = datetime.datetime.strptime(value, "%Y-%m-%d").date()
|
|
509
|
+
# Return the date as a string instead of a datetime object
|
|
510
|
+
return date_obj.strftime("%Y-%m-%d")
|
|
511
|
+
except ValueError:
|
|
512
|
+
# If the date is not in the correct format, return the original value
|
|
513
|
+
return value
|
|
514
|
+
|
|
515
|
+
# Add the custom date constructor
|
|
516
|
+
yaml_parser.constructor.add_constructor(
|
|
517
|
+
"tag:yaml.org,2002:timestamp", custom_date_constructor
|
|
518
|
+
)
|
|
519
|
+
|
|
520
|
+
return yaml_parser, reset_constructors
|
|
440
521
|
|
|
441
522
|
|
|
442
523
|
def _is_ascii(text: str) -> bool:
|
|
@@ -684,9 +765,6 @@ def validate_training_data_format_version(
|
|
|
684
765
|
parsed_version = version.parse(version_value)
|
|
685
766
|
latest_version = version.parse(LATEST_TRAINING_DATA_FORMAT_VERSION)
|
|
686
767
|
|
|
687
|
-
if isinstance(parsed_version, LegacyVersion):
|
|
688
|
-
raise TypeError
|
|
689
|
-
|
|
690
768
|
if parsed_version < latest_version:
|
|
691
769
|
raise_warning(
|
|
692
770
|
f"Training data file {filename} has a lower "
|
|
@@ -702,7 +780,7 @@ def validate_training_data_format_version(
|
|
|
702
780
|
if latest_version >= parsed_version:
|
|
703
781
|
return True
|
|
704
782
|
|
|
705
|
-
except TypeError:
|
|
783
|
+
except (TypeError, version.InvalidVersion):
|
|
706
784
|
raise_warning(
|
|
707
785
|
f"Training data file {filename} must specify "
|
|
708
786
|
f"'{KEY_TRAINING_DATA_FORMAT_VERSION}' as string, for example:\n"
|
|
@@ -784,3 +862,31 @@ def validate_yaml_with_jsonschema(
|
|
|
784
862
|
errors,
|
|
785
863
|
content=source_data,
|
|
786
864
|
)
|
|
865
|
+
|
|
866
|
+
|
|
867
|
+
def validate_yaml_data_using_schema_with_assertions(
|
|
868
|
+
yaml_data: Any,
|
|
869
|
+
schema_content: Union[List[Any], Dict[str, Any]],
|
|
870
|
+
package_name: str = PACKAGE_NAME,
|
|
871
|
+
) -> None:
|
|
872
|
+
"""Validate raw yaml content using a schema with assertions sub-schema.
|
|
873
|
+
|
|
874
|
+
Args:
|
|
875
|
+
yaml_data: the parsed yaml data to be validated
|
|
876
|
+
schema_content: the content of the YAML schema
|
|
877
|
+
package_name: the name of the package the schema is located in. defaults
|
|
878
|
+
to `rasa`.
|
|
879
|
+
"""
|
|
880
|
+
# test case assertions are part of the schema extension
|
|
881
|
+
# it will be included if the schema explicitly references it with
|
|
882
|
+
# include: assertions
|
|
883
|
+
e2e_test_cases_schema_content = read_schema_file(
|
|
884
|
+
ASSERTIONS_SCHEMA_FILE, package_name
|
|
885
|
+
)
|
|
886
|
+
|
|
887
|
+
schema_content = dict(schema_content, **e2e_test_cases_schema_content)
|
|
888
|
+
schema_extensions = [
|
|
889
|
+
str(files(package_name).joinpath(ASSERTIONS_SCHEMA_EXTENSIONS_FILE))
|
|
890
|
+
]
|
|
891
|
+
|
|
892
|
+
validate_yaml_content_using_schema(yaml_data, schema_content, schema_extensions)
|
rasa/studio/auth.py
CHANGED
|
@@ -21,15 +21,17 @@ from rasa.studio.results_logger import with_studio_error_handler, StudioResult
|
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
class StudioAuth:
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
) -> None:
|
|
24
|
+
"""Handles the authentication with the Rasa Studio authentication server."""
|
|
25
|
+
|
|
26
|
+
def __init__(self, studio_config: StudioConfig) -> None:
|
|
28
27
|
self.config = studio_config
|
|
28
|
+
verify = not studio_config.disable_verify
|
|
29
|
+
|
|
29
30
|
self.keycloak_openid = KeycloakOpenID(
|
|
30
31
|
server_url=studio_config.authentication_server_url,
|
|
31
32
|
client_id=studio_config.client_id,
|
|
32
33
|
realm_name=studio_config.realm_name,
|
|
34
|
+
verify=verify,
|
|
33
35
|
)
|
|
34
36
|
|
|
35
37
|
def health_check(self) -> bool:
|
rasa/studio/config.py
CHANGED
|
@@ -2,13 +2,14 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
4
|
from dataclasses import dataclass
|
|
5
|
-
from typing import Dict, Optional, Text
|
|
5
|
+
from typing import Any, Dict, Optional, Text
|
|
6
6
|
|
|
7
7
|
from rasa.utils.common import read_global_config_value, write_global_config_value
|
|
8
8
|
|
|
9
9
|
from rasa.studio.constants import (
|
|
10
10
|
RASA_STUDIO_AUTH_SERVER_URL_ENV,
|
|
11
11
|
RASA_STUDIO_CLI_CLIENT_ID_KEY_ENV,
|
|
12
|
+
RASA_STUDIO_CLI_DISABLE_VERIFY_KEY_ENV,
|
|
12
13
|
RASA_STUDIO_CLI_REALM_NAME_KEY_ENV,
|
|
13
14
|
RASA_STUDIO_CLI_STUDIO_URL_ENV,
|
|
14
15
|
STUDIO_CONFIG_KEY,
|
|
@@ -19,6 +20,7 @@ STUDIO_URL_KEY = "studio_url"
|
|
|
19
20
|
CLIENT_ID_KEY = "client_id"
|
|
20
21
|
REALM_NAME_KEY = "realm_name"
|
|
21
22
|
CLIENT_SECRET_KEY = "client_secret"
|
|
23
|
+
DISABLE_VERIFY = "disable_verify"
|
|
22
24
|
|
|
23
25
|
|
|
24
26
|
@dataclass
|
|
@@ -27,13 +29,15 @@ class StudioConfig:
|
|
|
27
29
|
studio_url: Optional[Text]
|
|
28
30
|
client_id: Optional[Text]
|
|
29
31
|
realm_name: Optional[Text]
|
|
32
|
+
disable_verify: bool = False
|
|
30
33
|
|
|
31
|
-
def to_dict(self) -> Dict[Text, Optional[
|
|
34
|
+
def to_dict(self) -> Dict[Text, Optional[Any]]:
|
|
32
35
|
return {
|
|
33
36
|
AUTH_SERVER_URL_KEY: self.authentication_server_url,
|
|
34
37
|
STUDIO_URL_KEY: self.studio_url,
|
|
35
38
|
CLIENT_ID_KEY: self.client_id,
|
|
36
39
|
REALM_NAME_KEY: self.realm_name,
|
|
40
|
+
DISABLE_VERIFY: self.disable_verify,
|
|
37
41
|
}
|
|
38
42
|
|
|
39
43
|
@classmethod
|
|
@@ -43,6 +47,7 @@ class StudioConfig:
|
|
|
43
47
|
studio_url=data[STUDIO_URL_KEY],
|
|
44
48
|
client_id=data[CLIENT_ID_KEY],
|
|
45
49
|
realm_name=data[REALM_NAME_KEY],
|
|
50
|
+
disable_verify=data.get(DISABLE_VERIFY, False),
|
|
46
51
|
)
|
|
47
52
|
|
|
48
53
|
def write_config(self) -> None:
|
|
@@ -73,7 +78,7 @@ class StudioConfig:
|
|
|
73
78
|
config = read_global_config_value(STUDIO_CONFIG_KEY, unavailable_ok=True)
|
|
74
79
|
|
|
75
80
|
if config is None:
|
|
76
|
-
return StudioConfig(None, None, None, None)
|
|
81
|
+
return StudioConfig(None, None, None, None, False)
|
|
77
82
|
|
|
78
83
|
if not isinstance(config, dict):
|
|
79
84
|
raise ValueError(
|
|
@@ -83,7 +88,7 @@ class StudioConfig:
|
|
|
83
88
|
)
|
|
84
89
|
|
|
85
90
|
for key in config:
|
|
86
|
-
if not isinstance(config[key], str):
|
|
91
|
+
if not isinstance(config[key], str) and key != DISABLE_VERIFY:
|
|
87
92
|
raise ValueError(
|
|
88
93
|
"Invalid config file format. "
|
|
89
94
|
f"Key '{key}' is not a text value."
|
|
@@ -102,6 +107,9 @@ class StudioConfig:
|
|
|
102
107
|
studio_url=StudioConfig._read_env_value(RASA_STUDIO_CLI_STUDIO_URL_ENV),
|
|
103
108
|
client_id=StudioConfig._read_env_value(RASA_STUDIO_CLI_CLIENT_ID_KEY_ENV),
|
|
104
109
|
realm_name=StudioConfig._read_env_value(RASA_STUDIO_CLI_REALM_NAME_KEY_ENV),
|
|
110
|
+
disable_verify=bool(
|
|
111
|
+
os.getenv(RASA_STUDIO_CLI_DISABLE_VERIFY_KEY_ENV, False)
|
|
112
|
+
),
|
|
105
113
|
)
|
|
106
114
|
|
|
107
115
|
@staticmethod
|
|
@@ -124,4 +132,5 @@ class StudioConfig:
|
|
|
124
132
|
studio_url=self.studio_url or other.studio_url,
|
|
125
133
|
client_id=self.client_id or other.client_id,
|
|
126
134
|
realm_name=self.realm_name or other.realm_name,
|
|
135
|
+
disable_verify=self.disable_verify or other.disable_verify,
|
|
127
136
|
)
|
rasa/studio/constants.py
CHANGED
|
@@ -10,6 +10,7 @@ RASA_STUDIO_AUTH_SERVER_URL_ENV = "RASA_STUDIO_AUTH_SERVER_URL"
|
|
|
10
10
|
RASA_STUDIO_CLI_STUDIO_URL_ENV = "RASA_STUDIO_CLI_STUDIO_URL"
|
|
11
11
|
RASA_STUDIO_CLI_REALM_NAME_KEY_ENV = "RASA_STUDIO_CLI_REALM_NAME_KEY"
|
|
12
12
|
RASA_STUDIO_CLI_CLIENT_ID_KEY_ENV = "RASA_STUDIO_CLI_CLIENT_ID_KEY"
|
|
13
|
+
RASA_STUDIO_CLI_DISABLE_VERIFY_KEY_ENV = "RASA_STUDIO_CLI_DISABLE_VERIFY_KEY"
|
|
13
14
|
|
|
14
15
|
STUDIO_NLU_FILENAME = "studio_nlu.yml"
|
|
15
16
|
STUDIO_DOMAIN_FILENAME = "studio_domain.yml"
|
rasa/studio/data_handler.py
CHANGED
|
@@ -76,7 +76,9 @@ class StudioDataHandler:
|
|
|
76
76
|
|
|
77
77
|
return request
|
|
78
78
|
|
|
79
|
-
def _make_request(
|
|
79
|
+
def _make_request(
|
|
80
|
+
self, GQL_req: Dict[Any, Any], verify: bool = True
|
|
81
|
+
) -> Dict[Any, Any]:
|
|
80
82
|
token = KeycloakTokenReader().get_token()
|
|
81
83
|
if token.is_expired():
|
|
82
84
|
token = self.refresh_token(token)
|
|
@@ -93,6 +95,7 @@ class StudioDataHandler:
|
|
|
93
95
|
"Authorization": f"{token.token_type} {token.access_token}",
|
|
94
96
|
"Content-Type": "application/json",
|
|
95
97
|
},
|
|
98
|
+
verify=verify,
|
|
96
99
|
)
|
|
97
100
|
|
|
98
101
|
if res.status_code != 200:
|
|
@@ -128,7 +131,9 @@ class StudioDataHandler:
|
|
|
128
131
|
The data from Rasa Studio.
|
|
129
132
|
"""
|
|
130
133
|
GQL_req = self._build_request()
|
|
131
|
-
|
|
134
|
+
verify = not self.studio_config.disable_verify
|
|
135
|
+
|
|
136
|
+
response = self._make_request(GQL_req, verify=verify)
|
|
132
137
|
self._extract_data(response)
|
|
133
138
|
|
|
134
139
|
def request_data(
|
|
@@ -145,7 +150,9 @@ class StudioDataHandler:
|
|
|
145
150
|
The data from Rasa Studio.
|
|
146
151
|
"""
|
|
147
152
|
GQL_req = self._build_request(intent_names, entity_names)
|
|
148
|
-
|
|
153
|
+
verify = not self.studio_config.disable_verify
|
|
154
|
+
|
|
155
|
+
response = self._make_request(GQL_req, verify=verify)
|
|
149
156
|
self._extract_data(response)
|
|
150
157
|
|
|
151
158
|
def get_config(self) -> Optional[str]:
|
rasa/studio/download.py
CHANGED
|
@@ -210,8 +210,8 @@ def _handle_download_no_overwrite(
|
|
|
210
210
|
if domain_path.is_dir():
|
|
211
211
|
studio_domain_path = domain_path / STUDIO_DOMAIN_FILENAME
|
|
212
212
|
new_domain_data = data_handler.combine_domains(
|
|
213
|
-
data_from_studio.
|
|
214
|
-
data_original.
|
|
213
|
+
data_from_studio.get_user_domain().as_dict(),
|
|
214
|
+
data_original.get_user_domain().as_dict(),
|
|
215
215
|
)
|
|
216
216
|
studio_domain = Domain.from_dict(new_domain_data)
|
|
217
217
|
if not studio_domain.is_empty():
|
|
@@ -219,7 +219,9 @@ def _handle_download_no_overwrite(
|
|
|
219
219
|
else:
|
|
220
220
|
logger.warning("No additional domain data found.")
|
|
221
221
|
elif domain_path.is_file():
|
|
222
|
-
domain_merged = data_original.
|
|
222
|
+
domain_merged = data_original.get_user_domain().merge(
|
|
223
|
+
data_from_studio.get_user_domain()
|
|
224
|
+
)
|
|
223
225
|
domain_merged.persist(domain_path)
|
|
224
226
|
|
|
225
227
|
if len(data_paths) == 1 and data_paths[0].is_file():
|
|
@@ -230,7 +232,9 @@ def _handle_download_no_overwrite(
|
|
|
230
232
|
)
|
|
231
233
|
data_nlu.persist_nlu(data_path)
|
|
232
234
|
if handler.has_flows():
|
|
233
|
-
data_flows = data_original.
|
|
235
|
+
data_flows = data_original.get_user_flows().merge(
|
|
236
|
+
data_from_studio.get_user_flows()
|
|
237
|
+
)
|
|
234
238
|
YamlFlowsWriter.dump(data_flows.underlying_flows, data_path)
|
|
235
239
|
|
|
236
240
|
elif len(data_paths) == 1 and data_paths[0].is_dir():
|
|
@@ -276,8 +280,8 @@ def _persist_flows_diff(
|
|
|
276
280
|
) -> None:
|
|
277
281
|
"""Creates a new flows file from the diff of original and studio data."""
|
|
278
282
|
new_flows_data = data_handler.create_new_flows_from_diff(
|
|
279
|
-
data_from_studio.
|
|
280
|
-
data_original.
|
|
283
|
+
data_from_studio.get_user_flows().underlying_flows,
|
|
284
|
+
data_original.get_user_flows().underlying_flows,
|
|
281
285
|
)
|
|
282
286
|
if new_flows_data:
|
|
283
287
|
YamlFlowsWriter.dump(new_flows_data, data_path)
|
|
@@ -317,11 +321,13 @@ def _handle_download_with_overwrite(
|
|
|
317
321
|
)
|
|
318
322
|
|
|
319
323
|
if domain_path.is_file():
|
|
320
|
-
domain_merged = data_from_studio.
|
|
324
|
+
domain_merged = data_from_studio.get_user_domain().merge(
|
|
325
|
+
data_original.get_user_domain()
|
|
326
|
+
)
|
|
321
327
|
domain_merged.persist(domain_path)
|
|
322
328
|
elif domain_path.is_dir():
|
|
323
329
|
default = domain_path / Path(STUDIO_DOMAIN_FILENAME)
|
|
324
|
-
studio_domain = data_from_studio.
|
|
330
|
+
studio_domain = data_from_studio.get_user_domain()
|
|
325
331
|
|
|
326
332
|
paths = get_domain_path(domain_path, data_from_studio, mapper)
|
|
327
333
|
|
|
@@ -358,8 +364,8 @@ def _handle_download_with_overwrite(
|
|
|
358
364
|
)
|
|
359
365
|
nlu_data_merged.persist_nlu(data_paths[0])
|
|
360
366
|
if handler.has_flows():
|
|
361
|
-
flows_data_merged = data_from_studio.
|
|
362
|
-
data_original.
|
|
367
|
+
flows_data_merged = data_from_studio.get_user_flows().merge(
|
|
368
|
+
data_original.get_user_flows()
|
|
363
369
|
)
|
|
364
370
|
YamlFlowsWriter.dump(flows_data_merged.underlying_flows, data_paths[0])
|
|
365
371
|
elif len(data_paths) == 1 and data_paths[0].is_dir():
|
|
@@ -374,12 +380,12 @@ def _handle_download_with_overwrite(
|
|
|
374
380
|
nlu_data = nlu_data.merge(nlu_file.get_nlu_data())
|
|
375
381
|
pretty_write_nlu_yaml(read_yaml(nlu_data.nlu_as_yaml()), paths["nlu_path"])
|
|
376
382
|
if handler.has_flows():
|
|
377
|
-
flows_data = data_from_studio.
|
|
383
|
+
flows_data = data_from_studio.get_user_flows()
|
|
378
384
|
if paths["flow_path"].exists():
|
|
379
385
|
flows_file = TrainingDataImporter.load_from_dict(
|
|
380
386
|
training_data_paths=[str(paths["flow_path"])]
|
|
381
387
|
)
|
|
382
|
-
flows_data = flows_data.merge(flows_file.
|
|
388
|
+
flows_data = flows_data.merge(flows_file.get_user_flows())
|
|
383
389
|
YamlFlowsWriter.dump(flows_data.underlying_flows, paths["flow_path"])
|
|
384
390
|
else:
|
|
385
391
|
# TODO: we are not handling the case of multiple data paths?
|
|
@@ -407,7 +413,7 @@ def get_training_path(
|
|
|
407
413
|
for intent in data_original.get_nlu_data().intents:
|
|
408
414
|
for path in mapper.get_file(intent, "intents").get("training", []):
|
|
409
415
|
nlu_paths.add(path)
|
|
410
|
-
flows = [flow.id for flow in data_original.
|
|
416
|
+
flows = [flow.id for flow in data_original.get_user_flows().underlying_flows]
|
|
411
417
|
for flow in flows:
|
|
412
418
|
for path in mapper.get_file(flow, "flows").get("training", []):
|
|
413
419
|
flow_paths.add(path)
|
rasa/studio/train.py
CHANGED
|
@@ -20,19 +20,18 @@ from rasa.shared.core.flows.yaml_flows_io import YamlFlowsWriter
|
|
|
20
20
|
from rasa.shared.importers.importer import TrainingDataImporter
|
|
21
21
|
from rasa.shared.utils.yaml import read_yaml, write_yaml
|
|
22
22
|
from rasa.studio import data_handler
|
|
23
|
-
from rasa.utils.common import get_temp_dir_name
|
|
24
|
-
|
|
25
23
|
from rasa.studio.config import StudioConfig
|
|
26
24
|
from rasa.studio.data_handler import (
|
|
27
25
|
StudioDataHandler,
|
|
28
26
|
import_data_from_studio,
|
|
29
27
|
)
|
|
28
|
+
from rasa.utils.common import get_temp_dir_name
|
|
30
29
|
|
|
31
30
|
logger = logging.getLogger(__name__)
|
|
32
31
|
|
|
33
32
|
|
|
34
33
|
def handle_train(args: argparse.Namespace) -> Optional[str]:
|
|
35
|
-
from rasa import train as train_all
|
|
34
|
+
from rasa.api import train as train_all
|
|
36
35
|
|
|
37
36
|
handler = StudioDataHandler(
|
|
38
37
|
studio_config=StudioConfig.read_config(), assistant_name=args.assistant_name[0]
|
rasa/studio/upload.py
CHANGED
|
@@ -56,7 +56,10 @@ def _get_selected_entities_and_intents(
|
|
|
56
56
|
|
|
57
57
|
def handle_upload(args: argparse.Namespace) -> None:
|
|
58
58
|
"""Uploads primitives to rasa studio."""
|
|
59
|
-
|
|
59
|
+
studio_config = StudioConfig.read_config()
|
|
60
|
+
endpoint = studio_config.studio_url
|
|
61
|
+
verify = not studio_config.disable_verify
|
|
62
|
+
|
|
60
63
|
if not endpoint:
|
|
61
64
|
rasa.shared.utils.cli.print_error_and_exit(
|
|
62
65
|
"No GraphQL endpoint found in config. Please run `rasa studio config`."
|
|
@@ -76,9 +79,9 @@ def handle_upload(args: argparse.Namespace) -> None:
|
|
|
76
79
|
|
|
77
80
|
# check safely if args.calm is set and not fail if not
|
|
78
81
|
if hasattr(args, "calm") and args.calm:
|
|
79
|
-
upload_calm_assistant(args, endpoint)
|
|
82
|
+
upload_calm_assistant(args, endpoint, verify=verify)
|
|
80
83
|
else:
|
|
81
|
-
upload_nlu_assistant(args, endpoint)
|
|
84
|
+
upload_nlu_assistant(args, endpoint, verify=verify)
|
|
82
85
|
|
|
83
86
|
|
|
84
87
|
config_keys = [
|
|
@@ -126,7 +129,9 @@ def _get_assistant_name(config: Dict[Text, Any]) -> str:
|
|
|
126
129
|
|
|
127
130
|
|
|
128
131
|
@with_studio_error_handler
|
|
129
|
-
def upload_calm_assistant(
|
|
132
|
+
def upload_calm_assistant(
|
|
133
|
+
args: argparse.Namespace, endpoint: str, verify: bool = True
|
|
134
|
+
) -> StudioResult:
|
|
130
135
|
"""Uploads the CALM assistant data to Rasa Studio.
|
|
131
136
|
|
|
132
137
|
Args:
|
|
@@ -151,7 +156,7 @@ def upload_calm_assistant(args: argparse.Namespace, endpoint: str) -> StudioResu
|
|
|
151
156
|
|
|
152
157
|
# Prepare config and domain
|
|
153
158
|
config = importer.get_config()
|
|
154
|
-
domain_from_files = importer.
|
|
159
|
+
domain_from_files = importer.get_user_domain().as_dict()
|
|
155
160
|
endpoints_from_files = read_yaml_file(args.endpoints)
|
|
156
161
|
config_from_files = read_yaml_file(args.config)
|
|
157
162
|
|
|
@@ -188,8 +193,7 @@ def upload_calm_assistant(args: argparse.Namespace, endpoint: str) -> StudioResu
|
|
|
188
193
|
training_data_paths=training_data_paths
|
|
189
194
|
)
|
|
190
195
|
|
|
191
|
-
|
|
192
|
-
flows = list(user_flows)
|
|
196
|
+
flows = list(flow_importer.get_user_flows())
|
|
193
197
|
|
|
194
198
|
# We instantiate the TrainingDataImporter again on purpose to avoid
|
|
195
199
|
# adding patterns to domain's actions. More info https://t.ly/W8uuc
|
|
@@ -217,11 +221,13 @@ def upload_calm_assistant(args: argparse.Namespace, endpoint: str) -> StudioResu
|
|
|
217
221
|
)
|
|
218
222
|
|
|
219
223
|
structlogger.info("Uploading to Rasa Studio...")
|
|
220
|
-
return make_request(endpoint, graphql_req)
|
|
224
|
+
return make_request(endpoint, graphql_req, verify)
|
|
221
225
|
|
|
222
226
|
|
|
223
227
|
@with_studio_error_handler
|
|
224
|
-
def upload_nlu_assistant(
|
|
228
|
+
def upload_nlu_assistant(
|
|
229
|
+
args: argparse.Namespace, endpoint: str, verify: bool = True
|
|
230
|
+
) -> StudioResult:
|
|
225
231
|
"""Uploads the classic (dm1) assistant data to Rasa Studio.
|
|
226
232
|
|
|
227
233
|
Args:
|
|
@@ -269,15 +275,16 @@ def upload_nlu_assistant(args: argparse.Namespace, endpoint: str) -> StudioResul
|
|
|
269
275
|
graphql_req = build_request(assistant_name, nlu_examples_yaml, domain_yaml)
|
|
270
276
|
|
|
271
277
|
structlogger.info("Uploading to Rasa Studio...")
|
|
272
|
-
return make_request(endpoint, graphql_req)
|
|
278
|
+
return make_request(endpoint, graphql_req, verify)
|
|
273
279
|
|
|
274
280
|
|
|
275
|
-
def make_request(endpoint: str, graphql_req: Dict) -> StudioResult:
|
|
281
|
+
def make_request(endpoint: str, graphql_req: Dict, verify: bool = True) -> StudioResult:
|
|
276
282
|
"""Makes a request to the studio endpoint to upload data.
|
|
277
283
|
|
|
278
284
|
Args:
|
|
279
285
|
endpoint: The studio endpoint
|
|
280
286
|
graphql_req: The graphql request
|
|
287
|
+
verify: Whether to verify SSL
|
|
281
288
|
"""
|
|
282
289
|
token = KeycloakTokenReader().get_token()
|
|
283
290
|
res = requests.post(
|
|
@@ -287,6 +294,7 @@ def make_request(endpoint: str, graphql_req: Dict) -> StudioResult:
|
|
|
287
294
|
"Authorization": f"{token.token_type} {token.access_token}",
|
|
288
295
|
"Content-Type": "application/json",
|
|
289
296
|
},
|
|
297
|
+
verify=verify,
|
|
290
298
|
)
|
|
291
299
|
|
|
292
300
|
if results_logger.response_has_errors(res.json()):
|