Example #1
0
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
Example #2
0
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
Example #3
0
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
Example #4
0
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
Example #5
0
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
Example #6
0
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
Example #7
0
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")]
Example #8
0
    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
Example #9
0
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
Example #10
0
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
Example #11
0
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
Example #12
0
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
Example #13
0
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}
Example #15
0
def test_action_registration():
    executor = ActionExecutor()
    executor.register_package("tests")
    assert CustomAction.name() in executor.actions
    assert CustomActionBase.name() not in executor.actions
Example #16
0
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)
Example #17
0
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,
    }