Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for different recipes #10641

Merged
merged 79 commits into from
Feb 2, 2022
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
78fc387
Add support for different recipes
Jan 6, 2022
661342c
Remove extra import
Jan 7, 2022
4554cc2
Fix changes so tests pass and backwards compatible
Jan 10, 2022
561b282
Merge branch 'main' into tayfun/10473-support-other-recipes
Jan 11, 2022
890812b
Graph recipe beginnings
Jan 11, 2022
e883752
Fix code climate
Jan 11, 2022
08da835
Fix auto_configure test failures
Jan 11, 2022
f78b775
Fix mypy errors
Jan 11, 2022
4b8d17a
Fix tests
Jan 11, 2022
4d1288a
Fix flake8 warnings
Jan 11, 2022
3f8f471
What happens in shared stays in shared
Jan 12, 2022
d967bc2
Merge branch 'main' into tayfun/10473-support-other-recipes
Jan 13, 2022
9332d63
Fix tests
Jan 13, 2022
495fc49
Fix mypy complaints
Jan 14, 2022
3306db5
Fix tests
Jan 14, 2022
a057cdc
Fix tests
Jan 14, 2022
fc19f83
Fix extra import
Jan 14, 2022
df2098e
No need to change auto-config template
Jan 14, 2022
c44ea8c
Merge branch 'main' into tayfun/10473-support-other-recipes
Jan 14, 2022
248d0ea
Mark as experimental and track recipe via telemetry
Jan 14, 2022
1b25fa3
Warn if CLI parameters for graph recipe
Jan 14, 2022
1ddc9be
Fix graph recipe
Jan 19, 2022
bcb659c
Merge branch 'main' into tayfun/10473-support-other-recipes
Jan 21, 2022
ef42783
Fix tests
Jan 21, 2022
3716a7d
Black reformat
Jan 21, 2022
a1fa40a
Update with new component
Jan 21, 2022
ae9f5e5
Fix registered component doc example
Jan 24, 2022
49ce5d6
Add docs for graph recipe
Jan 24, 2022
2293bca
Merge branch 'main' into tayfun/10473-support-other-recipes
Jan 24, 2022
ea70d41
Fix recipe docs URL in warning
Jan 24, 2022
47683f7
Fix example code path
Jan 24, 2022
2ef1118
Fix docs
Jan 24, 2022
8e2568a
Add changelog
Jan 24, 2022
f20c898
Fix docs
Jan 24, 2022
3e93547
Fix docs broken link
Jan 24, 2022
154056f
Merge branch 'main' into tayfun/10473-support-other-recipes
Jan 25, 2022
875a17d
Update docs/docs/graph-recipe.mdx
Jan 26, 2022
22e6e2c
Bump google-github-actions/setup-gcloud from 0.3.0 to 0.4.0
dependabot[bot] Jan 25, 2022
b6b3427
update endpoint and unit tests
ancalita Jan 26, 2022
dec37e0
add changelog
ancalita Jan 26, 2022
5ac4b08
add request timeout to cli command line arguments
donodje Jan 18, 2022
d4c8836
parse request timeout from command line args and set DEFAULT_REQUEST_…
donodje Jan 18, 2022
f44e014
test that the default request timeout argument is used properly
donodje Jan 18, 2022
ef6ce21
fix linter errors
donodje Jan 18, 2022
1f67c2f
directly access sys.argv to read the cmd line parameters
donodje Jan 18, 2022
b5fde26
remove trailing whitespace
donodje Jan 19, 2022
5330be7
run black to fix linter issues
donodje Jan 19, 2022
f79c756
deduplicate declaration of DEFAULT_RESPONSE_TIMEOUT and DEFAULT_REQUE…
donodje Jan 19, 2022
83ae794
correct constants import path in test
donodje Jan 19, 2022
3c15a3a
lint against upgraded black version 21.7b0
donodje Jan 19, 2022
74ab4b2
add changelog entry
donodje Jan 20, 2022
ad49502
set the request timeout from the request timeout command line arg
donodje Jan 25, 2022
094b49b
move DEFAULT_STREAM_READING_TIMEOUT to rasa.core.constants
donodje Jan 25, 2022
1f53e10
set default param for _get_stream_reading_timeout
donodje Jan 25, 2022
de72159
set None default for request_timeout parameter
donodje Jan 26, 2022
fc66008
Remove default schemas which is same for recipes
Jan 26, 2022
7368dd1
Merge branch 'main' into tayfun/10473-support-other-recipes
Jan 26, 2022
8304754
Update rasa/engine/recipes/default_recipe.py
Jan 26, 2022
f8ec9da
Make core|nlu_target to be configurable
Jan 28, 2022
1b9954a
Make tests clear; rewrite without monkeypatch
Jan 28, 2022
cb454dd
Fix typo
Jan 28, 2022
fa43bac
Add tip at the top for graph recipe docs
Jan 28, 2022
249a046
fix over-indent in Tokenizers section of docs (#10555)
Polaris000 Jan 28, 2022
1d3dc70
Merge branch 'main' into tayfun/10473-support-other-recipes
Jan 28, 2022
8e631e3
Fix mypy error
Jan 28, 2022
495fb46
Fix tests for auto-config
Jan 28, 2022
e5fa3ca
Update docs with target nodes
Jan 31, 2022
2b5af0f
Merge branch 'main' into tayfun/10473-support-other-recipes
Feb 1, 2022
fb9479b
Update docs/docs/graph-recipe.mdx
Feb 2, 2022
2bc6fe5
Move graph recipe to data folder for tests
Feb 2, 2022
9c771c8
Warn if multiple importers are used
Feb 2, 2022
b43566f
Add graph config schemas to telemetry
Feb 2, 2022
998da42
Fix changelog URL
Feb 2, 2022
3a491d4
Merge branch 'main' into tayfun/10473-support-other-recipes
Feb 2, 2022
16a4c24
Revert "Fix changelog URL"
Feb 2, 2022
fa1c2eb
Fix link to yet inexistent graph recipe docs
Feb 2, 2022
5efdb50
Add test for graph recipe telemetry event
Feb 2, 2022
b978569
Make targets required, raise if not provided
Feb 2, 2022
506f130
Fix mypy
Feb 2, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions data/test_config/config_response_selector_minimal.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,16 @@ pipeline:
- name: CountVectorsFeaturizer
- name: ResponseSelector
epochs: 1
policies:
# # No configuration for policies was provided. The following default policies were used to train your model.
# # If you'd like to customize them, uncomment and adjust the policies.
# # See https://rasa.com/docs/rasa/policies for more information.
# - name: MemoizationPolicy
# - name: RulePolicy
# - name: UnexpecTEDIntentPolicy
# max_history: 5
# epochs: 100
# - name: TEDPolicy
# max_history: 5
# epochs: 100
# constrain_similarities: true
2 changes: 1 addition & 1 deletion rasa/cli/interactive.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import rasa.cli.utils
from rasa.engine.storage.local_model_storage import LocalModelStorage
from rasa.shared.constants import DEFAULT_ENDPOINTS_PATH, DEFAULT_MODELS_PATH
from rasa.shared.importers.autoconfig import TrainingType
from rasa.shared.data import TrainingType
from rasa.shared.importers.importer import TrainingDataImporter
import rasa.shared.utils.cli
import rasa.utils.common
Expand Down
2 changes: 1 addition & 1 deletion rasa/cli/shell.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from rasa.cli.arguments import shell as arguments
from rasa.engine.storage.local_model_storage import LocalModelStorage
from rasa.model import get_latest_model
from rasa.shared.importers.autoconfig import TrainingType
from rasa.shared.data import TrainingType
from rasa.shared.utils.cli import print_error
from rasa.exceptions import ModelNotFound

Expand Down
2 changes: 1 addition & 1 deletion rasa/core/processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from rasa.engine.storage.local_model_storage import LocalModelStorage
from rasa.engine.storage.storage import ModelMetadata
from rasa.model import get_latest_model
from rasa.shared.importers.autoconfig import TrainingType
from rasa.shared.data import TrainingType
import rasa.shared.utils.io
import rasa.core.actions.action
from rasa.core import jobs
Expand Down
2 changes: 1 addition & 1 deletion rasa/engine/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

from rasa.engine.storage.storage import ModelStorage
from rasa.shared.exceptions import InvalidConfigException, RasaException
from rasa.shared.importers.autoconfig import TrainingType
from rasa.shared.data import TrainingType

logger = logging.getLogger(__name__)

Expand Down
273 changes: 270 additions & 3 deletions rasa/engine/recipes/default_recipe.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
CoreFeaturizationInputConverter,
CoreFeaturizationCollector,
)
from rasa.shared.exceptions import FileNotFoundException
from rasa.core.policies.ensemble import DefaultPolicyPredictionEnsemble

from rasa.engine.graph import (
Expand Down Expand Up @@ -41,17 +42,36 @@
from rasa.graph_components.providers.training_tracker_provider import (
TrainingTrackerProvider,
)
import rasa.shared.constants
from rasa.shared.exceptions import RasaException, InvalidConfigException
from rasa.shared.importers.autoconfig import TrainingType
from rasa.shared.data import TrainingType

from rasa.utils.tensorflow.constants import EPOCHS
import rasa.shared.utils.common
from rasa.shared.utils.common import (
class_from_module_path,
transform_collection_to_sentence,
)

logger = logging.getLogger(__name__)


DEFAULT_PREDICT_KWARGS = dict(constructor_name="load", eager=True, is_target=False,)

COMMENTS_FOR_KEYS = {
"pipeline": (
f"# # No configuration for the NLU pipeline was provided. The following "
f"default pipeline was used to train your model.\n"
f"# # If you'd like to customize it, uncomment and adjust the pipeline.\n"
f"# # See {rasa.shared.constants.DOCS_URL_PIPELINE} for more information.\n"
),
"policies": (
f"# # No configuration for policies was provided. The following default "
f"policies were used to train your model.\n"
f"# # If you'd like to customize them, uncomment and adjust the policies.\n"
f"# # See {rasa.shared.constants.DOCS_URL_POLICIES} for more information.\n"
),
}


class DefaultV1RecipeRegisterException(RasaException):
"""If you register a class which is not of type `GraphComponent`."""
Expand Down Expand Up @@ -144,7 +164,7 @@ def _from_registry(cls, name: Text) -> RegisteredComponent:
return cls._registered_components[name]

if "." in name:
clazz = rasa.shared.utils.common.class_from_module_path(name)
clazz = class_from_module_path(name)
if clazz.__name__ in cls._registered_components:
return cls._registered_components[clazz.__name__]

Expand Down Expand Up @@ -816,3 +836,250 @@ def _add_end_to_end_features_for_inference(
config={},
)
return node_with_e2e_features

@staticmethod
def auto_configure(
config_file_path: Optional[Text],
config: Dict,
training_type: Optional[TrainingType] = TrainingType.BOTH,
) -> Dict[Text, Any]:
"""Determine configuration from a configuration file.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe something more explicit about filling in gaps in a configuration file?


Keys that are provided and have a value in the file are kept. Keys that are not
provided are configured automatically.

Args:
config_file_path: The path to the configuration file.
config: Configuration in dictionary format.
training_type: Optional training type to auto-configure. By default
both core and NLU will be auto-configured.
"""
missing_keys = DefaultV1Recipe._get_missing_config_keys(config, training_type)
ka-bu marked this conversation as resolved.
Show resolved Hide resolved
keys_to_configure = DefaultV1Recipe._get_unspecified_autoconfigurable_keys(
config, training_type
)

if keys_to_configure:
config = DefaultV1Recipe.complete_config(config, keys_to_configure)
DefaultV1Recipe._dump_config(
config, config_file_path, missing_keys, keys_to_configure, training_type
)

return config

@staticmethod
def _get_unspecified_autoconfigurable_keys(
config: Dict[Text, Any],
training_type: Optional[TrainingType] = TrainingType.BOTH,
) -> Set[Text]:
if training_type == TrainingType.NLU:
all_keys = rasa.shared.constants.CONFIG_AUTOCONFIGURABLE_KEYS_NLU
elif training_type == TrainingType.CORE:
all_keys = rasa.shared.constants.CONFIG_AUTOCONFIGURABLE_KEYS_CORE
else:
all_keys = rasa.shared.constants.CONFIG_AUTOCONFIGURABLE_KEYS

return {k for k in all_keys if config.get(k) is None}
ka-bu marked this conversation as resolved.
Show resolved Hide resolved

@staticmethod
def _get_missing_config_keys(
config: Dict[Text, Any],
training_type: Optional[TrainingType] = TrainingType.BOTH,
) -> Set[Text]:
if training_type == TrainingType.NLU:
all_keys = rasa.shared.constants.CONFIG_KEYS_NLU
elif training_type == TrainingType.CORE:
all_keys = rasa.shared.constants.CONFIG_KEYS_CORE
else:
all_keys = rasa.shared.constants.CONFIG_KEYS

return {k for k in all_keys if k not in config.keys()}

@staticmethod
def complete_config(
config: Dict[Text, Any], keys_to_configure: Set[Text]
) -> Dict[Text, Any]:
"""Complete a config by adding automatic configuration for the specified keys.

Args:
config: The provided configuration.
keys_to_configure: Keys to be configured automatically (e.g. `policies`).

Returns:
The resulting configuration including both the provided and
the automatically configured keys.
"""
import pkg_resources

if keys_to_configure:
logger.debug(
f"The provided configuration does not contain the key(s) "
f"{transform_collection_to_sentence(keys_to_configure)}. " # noqa: E501, W505
f"Values will be provided from the default configuration."
)

filename = "config_files/default_config.yml"

default_config_file = pkg_resources.resource_filename(__name__, filename)
default_config = rasa.shared.utils.io.read_config_file(default_config_file)

config = copy.deepcopy(config)
for key in keys_to_configure:
config[key] = default_config[key]

return config

@staticmethod
def _dump_config(
config: Dict[Text, Any],
config_file_path: Text,
missing_keys: Set[Text],
auto_configured_keys: Set[Text],
training_type: Optional[TrainingType] = TrainingType.BOTH,
) -> None:
"""Dump the automatically configured keys into the config file.

The configuration provided in the file is kept as it is (preserving the order of
keys and comments).
For keys that were automatically configured, an explanatory
comment is added and the automatically chosen configuration is
added commented-out.
If there are already blocks with comments from a previous auto
configuration run, they are replaced with the new auto
configuration.

Args:
config: The configuration including the automatically configured keys.
config_file_path: The file into which the configuration should be dumped.
missing_keys: Keys that need to be added to the config file.
auto_configured_keys: Keys for which a commented out auto
configuration section needs to be added to the config file.
training_type: NLU, CORE or BOTH depending on which is trained.
"""
config_as_expected = DefaultV1Recipe._is_config_file_as_expected(
config_file_path, missing_keys, auto_configured_keys, training_type
)
if not config_as_expected:
rasa.shared.utils.cli.print_error(
ka-bu marked this conversation as resolved.
Show resolved Hide resolved
f"The configuration file at '{config_file_path}' has been removed or "
f"modified while the automatic configuration was running. The current "
f"configuration will therefore not be dumped to the file. If you want "
f"your model to use the configuration provided in "
f"'{config_file_path}' you need to re-run training."
)
return

DefaultV1Recipe._add_missing_config_keys_to_file(config_file_path, missing_keys)

autoconfig_lines = DefaultV1Recipe._get_commented_out_autoconfig_lines(
config, auto_configured_keys
)

current_config_content = rasa.shared.utils.io.read_file(config_file_path)
current_config_lines = current_config_content.splitlines(keepends=True)

updated_lines = DefaultV1Recipe._get_lines_including_autoconfig(
current_config_lines, autoconfig_lines
)

rasa.shared.utils.io.write_text_file("".join(updated_lines), config_file_path)

auto_configured_keys = transform_collection_to_sentence(auto_configured_keys)
rasa.shared.utils.cli.print_info(
f"The configuration for {auto_configured_keys} was chosen automatically. "
"It was written into the config file at '{config_file_path}'."
ka-bu marked this conversation as resolved.
Show resolved Hide resolved
)

@staticmethod
def _is_config_file_as_expected(
config_file_path: Text,
missing_keys: Set[Text],
auto_configured_keys: Set[Text],
training_type: Optional[TrainingType] = TrainingType.BOTH,
) -> bool:
try:
content = rasa.shared.utils.io.read_config_file(config_file_path)
except FileNotFoundException:
content = ""

return (
bool(content)
and missing_keys
== DefaultV1Recipe._get_missing_config_keys(content, training_type)
and auto_configured_keys
== DefaultV1Recipe._get_unspecified_autoconfigurable_keys(
content, training_type
)
)

@staticmethod
def _add_missing_config_keys_to_file(
config_file_path: Text, missing_keys: Set[Text]
) -> None:
if not missing_keys:
return
with open(
config_file_path, "a", encoding=rasa.shared.utils.io.DEFAULT_ENCODING
) as f:
for key in missing_keys:
f.write(f"{key}:\n")

@staticmethod
def _get_lines_including_autoconfig(
lines: List[Text], autoconfig_lines: Dict[Text, List[Text]]
) -> List[Text]:
auto_configured_keys = autoconfig_lines.keys()

lines_with_autoconfig = []
remove_comments_until_next_uncommented_line = False
for line in lines:
insert_section = None

# remove old auto configuration
if remove_comments_until_next_uncommented_line:
if line.startswith("#"):
continue
remove_comments_until_next_uncommented_line = False

# add an explanatory comment to auto configured sections
for key in auto_configured_keys:
if line.startswith(f"{key}:"): # start of next auto-section
line = line + COMMENTS_FOR_KEYS[key]
insert_section = key
remove_comments_until_next_uncommented_line = True

lines_with_autoconfig.append(line)

if not insert_section:
continue

# add the auto configuration (commented out)
lines_with_autoconfig += autoconfig_lines[insert_section]

return lines_with_autoconfig

@staticmethod
def _get_commented_out_autoconfig_lines(
config: Dict[Text, Any], auto_configured_keys: Set[Text]
) -> Dict[Text, List[Text]]:
import ruamel.yaml
import ruamel.yaml.compat

yaml_parser = ruamel.yaml.YAML()
yaml_parser.indent(mapping=2, sequence=4, offset=2)

autoconfig_lines = {}

for key in auto_configured_keys:
stream = ruamel.yaml.compat.StringIO()
yaml_parser.dump(config.get(key), stream)
dump = stream.getvalue()

lines = dump.split("\n")
if not lines[-1]:
lines = lines[:-1] # yaml dump adds an empty line at the end
lines = [f"# {line}\n" for line in lines]

autoconfig_lines[key] = lines

return autoconfig_lines
20 changes: 20 additions & 0 deletions rasa/engine/recipes/graph_recipe.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from rasa.engine.recipes.recipe import Recipe
from rasa.engine.graph import GraphModelConfiguration
from rasa.shared.data import TrainingType

from typing import Dict, Text, Any


class GraphV1Recipe(Recipe):
"""Recipe which converts the graph model config to train and predict graph."""

name = "graph.v1"

def graph_config_for_recipe(
self,
config: Dict,
cli_parameters: Dict[Text, Any],
training_type: TrainingType = TrainingType.BOTH,
joejuzl marked this conversation as resolved.
Show resolved Hide resolved
is_finetuning: bool = False,
) -> GraphModelConfiguration:
"""Converts the default config to graphs (see interface for full docstring)."""
Loading