diff --git a/changelog/6276.improvement.md b/changelog/6276.improvement.md new file mode 100644 index 000000000000..5955add6d750 --- /dev/null +++ b/changelog/6276.improvement.md @@ -0,0 +1 @@ +Allow Rasa to boot when model loading exception occurs. Forward HTTP Error responses to standard log output. diff --git a/rasa/core/agent.py b/rasa/core/agent.py index c41a530ef743..bb81db67fae3 100644 --- a/rasa/core/agent.py +++ b/rasa/core/agent.py @@ -80,9 +80,7 @@ def _load_interpreter( The NLU interpreter. """ if nlu_path: - from rasa.core.interpreter import RasaNLUInterpreter - - return RasaNLUInterpreter(model_directory=nlu_path) + return NaturalLanguageInterpreter.create(nlu_path) return agent.interpreter or RegexInterpreter() diff --git a/rasa/core/run.py b/rasa/core/run.py index a06f6d9930dc..c7f13a1cd4c7 100644 --- a/rasa/core/run.py +++ b/rasa/core/run.py @@ -23,6 +23,7 @@ from rasa.core.utils import AvailableEndpoints from rasa.utils.common import raise_warning from sanic import Sanic +from asyncio import AbstractEventLoop logger = logging.getLogger() # get the root logger @@ -224,7 +225,7 @@ async def load_agent_on_start( endpoints: AvailableEndpoints, remote_storage: Optional[Text], app: Sanic, - loop: Text, + loop: AbstractEventLoop, ): """Load an agent. @@ -246,16 +247,22 @@ async def load_agent_on_start( model_server = endpoints.model if endpoints and endpoints.model else None - app.agent = await agent.load_agent( - model_path, - model_server=model_server, - remote_storage=remote_storage, - interpreter=_interpreter, - generator=endpoints.nlg, - tracker_store=_tracker_store, - lock_store=_lock_store, - action_endpoint=endpoints.action, - ) + try: + app.agent = await agent.load_agent( + model_path, + model_server=model_server, + remote_storage=remote_storage, + interpreter=_interpreter, + generator=endpoints.nlg, + tracker_store=_tracker_store, + lock_store=_lock_store, + action_endpoint=endpoints.action, + ) + except Exception as e: + raise_warning( + f"The model at '{model_path}' could not be loaded. " f"Error: {e}" + ) + app.agent = None if not app.agent: raise_warning( diff --git a/rasa/server.py b/rasa/server.py index ffc4d9e7b0b1..d05660d627fd 100644 --- a/rasa/server.py +++ b/rasa/server.py @@ -82,6 +82,7 @@ def __init__( "code": status, } self.status = status + logger.error(message) def _docs(sub_url: Text) -> Text: diff --git a/tests/core/test_run.py b/tests/core/test_run.py index d11099747607..de8df1c85d7b 100644 --- a/tests/core/test_run.py +++ b/tests/core/test_run.py @@ -1,4 +1,10 @@ -from rasa.core import run +import pytest +from typing import Text +from sanic import Sanic +from asyncio import AbstractEventLoop +from pathlib import Path +from rasa.core import run, interpreter, policies, domain +from rasa.core.utils import AvailableEndpoints CREDENTIALS_FILE = "examples/moodbot/credentials.yml" @@ -40,3 +46,36 @@ def test_create_single_input_channels_by_class_wo_credentials(): assert len(channels) == 1 assert channels[0].name() == "rest" + + +async def test_load_agent_on_start_with_good_model_file( + trained_rasa_model: Text, rasa_server: Sanic, loop: AbstractEventLoop, +): + agent = await run.load_agent_on_start( + trained_rasa_model, AvailableEndpoints(), None, rasa_server, loop, + ) + + assert isinstance(agent.interpreter, interpreter.RasaNLUInterpreter) + assert isinstance(agent.policy_ensemble, policies.PolicyEnsemble) + assert isinstance(agent.domain, domain.Domain) + + +async def test_load_agent_on_start_with_bad_model_file( + tmp_path: Path, rasa_server: Sanic, loop: AbstractEventLoop, +): + fake_model = tmp_path / "fake_model.tar.gz" + fake_model.touch() + fake_model_path = str(fake_model) + + with pytest.warns(UserWarning) as warnings: + agent = await run.load_agent_on_start( + fake_model_path, AvailableEndpoints(), None, rasa_server, loop, + ) + assert any( + "fake_model.tar.gz' could not be loaded" in str(w.message) for w in warnings + ) + + # Fallback agent was loaded even if model was unusable + assert isinstance(agent.interpreter, interpreter.RegexInterpreter) + assert agent.policy_ensemble is None + assert isinstance(agent.domain, domain.Domain)