def test_load_action_from_init(executor: ActionExecutor, package_path: Text): # Actions should be loaded from packages _write_action_file(package_path, "__init__.py", "InitAction", "init_action") executor.register_package(package_path.replace("/", ".")) assert "init_action" in executor.actions
def test_load_abstract_classes(executor: ActionExecutor, package_path: Text): ABSTRACT_ACTION_TEST_TEMPLATE = """ from abc import ABC, abstractmethod from rasa_sdk import Action class AbstractTestAction(ABC, Action): @abstractmethod def name(self): return "abstract_test_action" @abstractmethod async def run(self, dispatcher, tracker, domain): dispatcher.utter_message("abstract test action") return [] class TestConcreteAction(AbstractTestAction): def name(self): return "concrete_test_action" async def run(self, dispatcher, tracker, domain): dispatcher.utter_message("concrete test action") return [] """ # set up the file with open(os.path.join(package_path, "__init__.py"), "w") as f: f.write(ABSTRACT_ACTION_TEST_TEMPLATE) executor.register_package(package_path.replace("/", ".")) # abstract_test_action shouldn't exist in this executor actions list assert "abstract_test_action" not in executor.actions assert "concrete_test_action" in executor.actions
def test_load_module_directly(executor: ActionExecutor, package_path: Text): _write_action_file(package_path, "test_module.py", "TestModuleAction", "test_module_action") # Load a module directly, not a package. This is equivalent to loading # "action" when there's an "action.py" file in the module search path. executor.register_package(package_path.replace("/", ".") + ".test_module") assert "test_module_action" in executor.actions
def test_load_submodules_in_namespace_package(executor: ActionExecutor, package_path: Text): # If there's submodules inside a namespace package (a package without # __init__.py), they should be picked up correctly _write_action_file(package_path, "foo.py", "FooAction", "foo_action") _write_action_file(package_path, "bar.py", "BarAction", "bar_action") executor.register_package(package_path.replace("/", ".")) for name in ["foo_action", "bar_action"]: assert name in executor.actions
def test_load_submodules_in_package(executor: ActionExecutor, package_path: Text): # If there's submodules inside a package, they should be picked up correctly _write_action_file(package_path, "__init__.py", "Action1", "action1") _write_action_file(package_path, "a1.py", "Action2", "action2") _write_action_file(package_path, "a2.py", "Action3", "action3") executor.register_package(package_path.replace("/", ".")) for name in ["action1", "action2", "action3"]: assert name in executor.actions
class ActionRunner(object): def __init__(self, domain_path='agents/prototypes/concertbot/domain.yml'): self.executor = ActionExecutor() action_package_name = 'actions.procs' self.executor.register_package(action_package_name) endpoint = EndpointConfig("http://localhost:5000") self.interpreter = RasaNLUHttpInterpreter(model_name="nlu", project_name='chinese', endpoint=endpoint) self.domain = Domain.load(domain_path) def create_api_response(self, events, messages): return {"events": events if events else [], "responses": messages} def prepare(self, text): tracker = DialogueStateTracker("default", self.domain.slots) parse_data = self.interpreter.parse(text) # print(parse_data) tracker.update( UserUttered(text, parse_data["intent"], parse_data["entities"], parse_data)) # store all entities as slots for e in self.domain.slots_for_entities(parse_data["entities"]): tracker.update(e) print("Logged UserUtterance - " "tracker now has {} events".format(len(tracker.events))) # print(tracker.latest_message) return tracker def execute(self, action_name, text, get_tracker=False): """ $ python -m sagas.bots.action_runner execute action_about_date '找音乐会' $ python -m sagas.bots.action_runner execute action_about_date '找音乐会' True $ python -m sagas.bots.action_runner execute action_joke '找音乐会' :param action_name: :param text: :return: """ # tracker = DialogueStateTracker("default", domain.slots) tracker = self.prepare(text) dispatcher = CollectingDispatcher() action = self.executor.actions.get(action_name) events = action(dispatcher, tracker, self.domain) resp = self.create_api_response(events, dispatcher.messages) if get_tracker: evs = deserialise_events(events) for ev in evs: tracker.update(ev) return resp, tracker else: return resp
def test_abstract_action(): executor = ActionExecutor() executor.register_package("tests") assert CustomAction.name() in executor.actions assert CustomActionBase.name() not in executor.actions dispatcher = CollectingDispatcher() tracker = Tracker("test", {}, {}, [], False, None, {}, "listen") domain = {} events = CustomAction().run(dispatcher, tracker, domain) assert events == [SlotSet("test", "test")]
def get_actions(self, location): if os.path.isdir(location): sys.path.insert(0, location) a_exec = ActionExecutor() # os.path.basename did not work here for some reason unknown. a_exec.register_package(Path(location).name) actions = list(a_exec.actions.keys()) # Return a dictionary of actions and forms # NOTE: FORM NEEDS TO HAVE FORM IN NAME, ACTIONS NEED ACTION IN NAME return {"actions": [item for item in actions if item.startswith("action")], "forms": [item for item in actions if item.startswith("form")]} elif os.path.isfile(location): logger.error("%s must be a model directory, not a file" % location) return None
def create_app( action_package_name: Union[Text, types.ModuleType], cors_origins: Union[Text, List[Text], None] = "*", ) -> Sanic: app = Sanic(__name__, configure_logging=False) configure_cors(app, cors_origins) executor = ActionExecutor() executor.register_package(action_package_name) @app.get("/health") async def health(_) -> HTTPResponse: """Ping endpoint to check if the server is running and well.""" body = {"status": "ok"} return response.json(body, status=200) @app.post("/webhook") async def webhook(request: Request) -> HTTPResponse: """Webhook to retrieve action calls.""" action_call = request.json if action_call is None: body = {"error": "Invalid body request"} return response.json(body, status=400) utils.check_version_compatibility(action_call.get("version")) try: result = await executor.run(action_call) except ActionExecutionRejection as e: logger.error(e) body = {"error": e.message, "action_name": e.action_name} return response.json(body, status=400) except ActionNotFoundException as e: logger.error(e) body = {"error": e.message, "action_name": e.action_name} return response.json(body, status=404) return response.json(result, status=200) @app.get("/actions") async def actions(_) -> HTTPResponse: """List all registered actions.""" body = [{"name": k} for k in executor.actions.keys()] return response.json(body, status=200) return app
def endpoint_app(cors_origins=None, action_package_name=None): app = Flask(__name__) if not cors_origins: cors_origins = [] executor = ActionExecutor() executor.register_package(action_package_name) CORS(app, resources={r"/*": {"origins": cors_origins}}) @app.route("/health", methods=["GET", "OPTIONS"]) @cross_origin(origins=cors_origins) def health(): """Ping endpoint to check if the server is running and well.""" return jsonify({"status": "ok"}) @app.route("/webhook", methods=["POST", "OPTIONS"]) @cross_origin() def webhook(): """Webhook to retrieve action calls.""" action_call = request.json check_version_compatibility(action_call.get("version")) try: response = executor.run(action_call) except ActionExecutionRejection as e: logger.error(str(e)) result = {"error": str(e), "action_name": e.action_name} response = jsonify(result) response.status_code = 400 return response return jsonify(response) @app.route("/actions", methods=["GET", "OPTIONS"]) @cross_origin(origins=cors_origins) def actions(): """List all registered actions.""" return jsonify([{"name": k} for k in executor.actions.keys()]) return app
def create_app( action_package_name: Union[Text, types.ModuleType], cors_origins: Union[Text, List[Text], None] = "*", ) -> Sanic: app = Sanic(__name__, configure_logging=False) configure_cors(app, cors_origins) executor = ActionExecutor() executor.register_package(action_package_name) @app.get("/health") async def health(request): """Ping endpoint to check if the server is running and well.""" return create_ok_response({"status": "ok"}) @app.post("/webhook") async def webhook(request): """Webhook to retrieve action calls.""" action_call = request.json utils.check_version_compatibility(action_call.get("version")) try: result = await executor.run(action_call) except ActionExecutionRejection as e: logger.error(str(e)) return create_error_response(str(e), e.action_name, 400) except ActionNotFoundRejection as e: logger.error(str(e)) return create_error_response(str(e), e.action_name, 404) return create_ok_response(result) @app.get("/actions") async def actions(request): """List all registered actions.""" return create_ok_response([{ "name": k } for k in executor.actions.keys()]) return app
def create_app( action_package_name: Union[Text, types.ModuleType], cors_origins: Union[Text, List[Text], None] = "*", auto_reload: bool = False, ) -> Sanic: """Create a Sanic application and return it. Args: action_package_name: Name of the package or module to load actions from. cors_origins: CORS origins to allow. auto_reload: When `True`, auto-reloading of actions is enabled. Returns: A new Sanic application ready to be run. """ app = Sanic(__name__, configure_logging=False) configure_cors(app, cors_origins) executor = ActionExecutor() executor.register_package(action_package_name) @app.get("/health") async def health(_) -> HTTPResponse: """Ping endpoint to check if the server is running and well.""" body = {"status": "ok"} return response.json(body, status=200) @app.post("/webhook") async def webhook(request: Request) -> HTTPResponse: """Webhook to retrieve action calls.""" action_call = request.json if action_call is None: body = {"error": "Invalid body request"} return response.json(body, status=400) utils.check_version_compatibility(action_call.get("version")) if auto_reload: executor.reload() try: result = await executor.run(action_call) except ActionExecutionRejection as e: logger.debug(e) body = {"error": e.message, "action_name": e.action_name} return response.json(body, status=400) except ActionNotFoundException as e: logger.error(e) body = {"error": e.message, "action_name": e.action_name} return response.json(body, status=404) return response.json(result, status=200) @app.get("/actions") async def actions(_) -> HTTPResponse: """List all registered actions.""" if auto_reload: executor.reload() body = [{"name": k} for k in executor.actions.keys()] return response.json(body, status=200) @app.get('/download-file/<file_id:str>') async def download_file(request: Request) -> HTTPResponse: from crawler.models import Attachment, get_session, close_session session = get_session() attachment: Attachment = None try: attachment = session.query(Attachment).filter_by(id=file_id).first() if not attachment: body = {"error": 'Arquivo não encontrado', "action_name": e.action_name} return response.json(body, status=400) except Exception as err: body = {"error": err.message, "action_name": e.action_name} return response.json(body, status=400) finally: close_session(session) return await response.file(attachment.path) return app
def create_app( action_package_name: Union[Text, types.ModuleType], cors_origins: Union[Text, List[Text], None] = "*", auto_reload: bool = False, ) -> Sanic: """Create a Sanic application and return it. Args: action_package_name: Name of the package or module to load actions from. cors_origins: CORS origins to allow. auto_reload: When `True`, auto-reloading of actions is enabled. Returns: A new Sanic application ready to be run. """ app = Sanic(__name__, configure_logging=False) configure_cors(app, cors_origins) executor = ActionExecutor() executor.register_package(action_package_name) @app.get("/health") async def health(_) -> HTTPResponse: """Ping endpoint to check if the server is running and well.""" body = {"status": "ok"} return response.json(body, status=200) @app.post("/webhook") async def webhook(request: Request) -> HTTPResponse: """Webhook to retrieve action calls.""" action_call = request.json if action_call is None: body = {"error": "Invalid body request"} return response.json(body, status=400) utils.check_version_compatibility(action_call.get("version")) if auto_reload: executor.reload() try: result = await executor.run(action_call) except ActionExecutionRejection as e: logger.debug(e) body = {"error": e.message, "action_name": e.action_name} return response.json(body, status=400) except ActionNotFoundException as e: logger.error(e) body = {"error": e.message, "action_name": e.action_name} return response.json(body, status=404) return response.json(result, status=200) @app.get("/actions") async def actions(_) -> HTTPResponse: """List all registered actions.""" if auto_reload: executor.reload() body = [{"name": k} for k in executor.actions.keys()] return response.json(body, status=200) return app
from django.http import HttpResponse, HttpResponseBadRequest, Http404 from django.shortcuts import get_object_or_404 from django.core.exceptions import SuspiciousOperation import json import logging import random from asgiref.sync import async_to_sync from . import models from django.views.decorators.csrf import csrf_exempt from rasa_sdk.executor import ActionExecutor from rasa_sdk.interfaces import ActionExecutionRejection logger = logging.getLogger(__name__) executor = ActionExecutor() executor.register_package("rasa_api.actions") @csrf_exempt def webhook(request): try: data = json.loads(request.body) except json.JSONDecodeError: raise SuspiciousOperation() # logger.debug(f"Got event from rasa webhook: {pprint.pformat(data)}") try: out_data = json.dumps(async_to_sync(executor.run)(data)) except ActionExecutionRejection as e: logger.error(str(e)) result = {"error": str(e), "action_name": e.action_name}
def test_action_registration(): executor = ActionExecutor() executor.register_package("tests") assert CustomAction.name() in executor.actions assert CustomActionBase.name() not in executor.actions
from rasa_sdk.executor import ActionExecutor from rasa_sdk.interfaces import ActionExecutionRejection import sys sys.path.append('.') from utils.bot_factory import BotFactory logging.basicConfig( level=logging.WARN, format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s', datefmt='%m-%d %H:%M') # Action executor config executor = ActionExecutor() executor.register_package('actions') # Home page async def index(request): index_file = open('examples/coffee_bot/templates/index.html') return web.Response(body=index_file.read().encode('utf-8'), headers={'content-type': 'text/html'}) # Action endpoint async def webhook(request): """Webhook to retrieve action calls.""" action_call = await request.json() try: response = await executor.run(action_call)
async def test_reload_module(executor: ActionExecutor, dispatcher: CollectingDispatcher, package_path: Text): action_class = "MyAction" action_file = "my_action.py" action_name = "my_action" _write_action_file(package_path, action_file, action_class, action_name, message="foobar") executor.register_package(package_path.replace("/", ".")) action_v1 = executor.actions.get(action_name) assert action_v1 await action_v1(dispatcher, None, None) assert dispatcher.messages[0] == { "text": "foobar", "buttons": [], "elements": [], "custom": {}, "template": None, "image": None, "attachment": None, } # Write the action file again, but change its contents _write_action_file(package_path, action_file, action_class, action_name, message="hello!") # Manually set the file's timestamp 10 seconds into the future, otherwise # Python will load the class already compiled in __pycache__, which is the # previous version. mod_time = time.time() + 10 os.utime(os.path.join(package_path, action_file), times=(mod_time, mod_time)) # Reload modules executor.reload() dispatcher.messages.clear() action_v2 = executor.actions.get(action_name) assert action_v2 await action_v2(dispatcher, None, None) # The message should have changed assert dispatcher.messages[0] == { "text": "hello!", "buttons": [], "elements": [], "custom": {}, "template": None, "image": None, "attachment": None, }