Exemple #1
0
    async def get_model_by_name(request, project_id, model):
        model = _model_service(request).get_model_by_name(project_id, model)
        if not model:
            return rasa_x_utils.error(
                http.HTTPStatus.NOT_FOUND,
                "ModelNotFound",
                f"Model '{model}' not found for project '{project_id}'.",
            )
        model_hash = model["hash"]
        try:
            if model_hash == request.headers.get("If-None-Match"):
                return response.text("", http.HTTPStatus.NO_CONTENT)

            return await response.file_stream(
                location=model["path"],
                headers={
                    "ETag": model_hash,
                    "filename": os.path.basename(model["path"]),
                },
                mime_type="application/gzip",
            )
        except FileNotFoundError as e:
            logger.exception(e)
            return rasa_x_utils.error(
                http.HTTPStatus.NOT_FOUND,
                "ModelDownloadFailed",
                f"Failed to download file.\n{e}",
            )
    async def get_story(request, story_id):
        from rasa.core.domain import Domain

        story = _story_service(request).fetch_story(story_id)

        content_type = request.headers.get("Accept")
        if content_type == "text/vnd.graphviz":
            project_id = rasa_x_utils.default_arg(request, "project_id",
                                                  config.project_name)
            domain_dict = _domain_service(request).get_or_create_domain(
                project_id)
            domain = Domain.from_dict(domain_dict)

            visualization = await _story_service(request).visualize_stories(
                [story], domain)

            if visualization:
                return response.text(visualization)
            else:
                return rasa_x_utils.error(
                    HTTPStatus.NOT_ACCEPTABLE,
                    "VisualizationNotAvailable",
                    "Cannot produce a visualization for the requested story",
                )
        else:
            if story:
                return response.json(story)

        return rasa_x_utils.error(
            HTTPStatus.NOT_FOUND,
            "StoryNotFound",
            f"Story for id {story_id} could not be found",
        )
Exemple #3
0
    async def get_model_for_tag(request, project_id, tag):
        model = _model_service(request).model_for_tag(project_id, tag)
        if not model:
            return rasa_x_utils.error(
                404, "TagNotFound",
                f"Tag '{tag}' not found for project '{project_id}'.")
        model_hash = model["hash"]
        try:
            if model_hash == request.headers.get("If-None-Match"):
                return response.text("", http.HTTPStatus.NO_CONTENT)

            return await response.file_stream(
                location=model["path"],
                headers={
                    "ETag": model_hash,
                    "filename": os.path.basename(model["path"]),
                },
                mime_type="application/gzip",
            )
        except FileNotFoundError:
            logger.warning("Tried to download model file '{}', "
                           "but file does not exist.".format(model["path"]))
            return rasa_x_utils.error(
                404,
                "ModelDownloadFailed",
                "Failed to find model file '{}'.".format(model["path"]),
            )
    async def update_conversation_evaluations(request):
        event_service = _event_service(request)
        conversation_ids = event_service.conversation_ids()
        for _id in conversation_ids:
            story = event_service.story_for_conversation_id(_id)
            try:
                content = await _stack_service(request,
                                               "worker").evaluate_story(story)
                event_service.update_evaluation(_id, content)
            except ClientError as e:
                return rasa_x_utils.error(
                    HTTPStatus.BAD_REQUEST,
                    "CoreEvaluationFailed",
                    f"Failed to create evaluation for conversation with ID '{_id}'.",
                    details=e,
                )
            except ValueError as e:
                return rasa_x_utils.error(
                    HTTPStatus.NOT_FOUND,
                    "StoringEvaluationFailed",
                    f"Failed to store evaluation for conversation_id '{_id}'. Conversation not found.",
                    details=e,
                )

        return response.text("", HTTPStatus.NO_CONTENT)
    async def post_event(request, conversation_id, user):
        _event = request.json
        try:
            _ = await _stack_service(
                request, RASA_PRODUCTION_ENVIRONMENT).append_events_to_tracker(
                    conversation_id, _event)

            if tracker_utils.is_user_event(_event):
                # add annotated user messages to training data
                data_service = DataService(request[REQUEST_DB_SESSION_KEY])
                data_service.save_user_event_as_example(
                    user, config.project_name, _event)

                telemetry.track_message_annotated(
                    telemetry.MESSAGE_ANNOTATED_INTERACTIVE_LEARNING)

            return response.json(_event)
        except ClientError as e:
            logger.warning(
                f"Creating event for sender '{conversation_id}' failed. Error: {e}"
            )
            return rasa_x_utils.error(
                HTTPStatus.BAD_REQUEST,
                "EventCreationError",
                f"Error when creating event for sender '{conversation_id}'.",
                details=e,
            )
        except ValueError as e:
            return rasa_x_utils.error(HTTPStatus.NOT_FOUND,
                                      "ServiceNotFound",
                                      details=e)
    async def chat_to_bot(request, conversation_id=None):
        message_object = request.json

        if conversation_id:
            message_object["conversation_id"] = conversation_id

        environment = rasa_x_utils.deployment_environment_from_request(
            request, DEFAULT_RASA_ENVIRONMENT)
        service = _stack_service(request, DEFAULT_RASA_ENVIRONMENT)
        if not service:
            return rasa_x_utils.error(
                HTTPStatus.NOT_FOUND,
                "ServiceNotFound",
                f"Service for requested environment '{environment}' not found.",
            )
        try:
            message_response = await service.send_message(
                message_object, token=request.headers.get("Authorization"))
            return response.json(message_response)
        except ClientError as e:
            logger.error(
                f"Failed to send message to Rasa Chat webhook. Error: {e}")
            return rasa_x_utils.error(
                HTTPStatus.NOT_FOUND,
                "MessageSendFailed",
                "Message '{}' could not be sent to environment '{}'.".format(
                    message_object["message"], environment),
            )
    async def put_evaluation(request, conversation_id):
        event_service = _event_service(request)
        story = event_service.story_for_conversation_id(conversation_id)

        if not story:
            return rasa_x_utils.error(
                HTTPStatus.NOT_FOUND,
                "ClientNotFound",
                f"Client for conversation_id {conversation_id} could not be found",
            )

        try:
            content = await _stack_service(request,
                                           "worker").evaluate_story(story)
            event_service.update_evaluation(conversation_id, content)

            return response.json(content)
        except ClientError as e:
            return rasa_x_utils.error(
                HTTPStatus.BAD_REQUEST,
                "CoreEvaluationFailed",
                f"Failed to create evaluation for conversation_id {conversation_id}",
                details=e,
            )
        except ValueError as e:
            return rasa_x_utils.error(
                HTTPStatus.NOT_FOUND,
                "StoringEvaluationFailed",
                f"Failed to store evaluation for conversation_id '{conversation_id}'.",
                details=e,
            )
    async def create_commit(request: Request, project_id: Text,
                            repository_id: int,
                            branch_name: Text) -> HTTPResponse:
        """Commit and push the current local changes."""

        git_service = _git(request, project_id, repository_id)

        try:
            commit = await git_service.commit_and_push_changes_to(branch_name)
            return response.json(commit, 201)
        except ValueError as e:
            logger.debug(e)
            return error(
                404,
                "RepositoryNotFound",
                f"Branch '{branch_name}' for repository with ID '{repository_id}' "
                f"could not be found.",
            )
        except GitCommitError as e:
            logger.debug(e)
            return error(
                403,
                "BranchIsProtected",
                f"Branch '{branch_name}' is protected. Please add your changes to a "
                f"different branch.",
            )
    async def get_conversation_tracker_impl(request: Request,
                                            conversation_id: Text,
                                            user: Dict[Text, Any] = None):
        event_service = _event_service(request)

        if not _has_access_to_conversation(event_service, conversation_id,
                                           user):
            return rasa_x_utils.error(HTTPStatus.UNAUTHORIZED, "NoPermission",
                                      "Access denied")

        until_time = rasa_x_utils.float_arg(request, "until", None)
        since_time = rasa_x_utils.float_arg(request, "since", None)
        rasa_environment_query = rasa_x_utils.default_arg(
            request, "rasa_environment", DEFAULT_RASA_ENVIRONMENT)
        event_verbosity = _event_verbosity_from_request(request)
        exclude_leading_action_session_start = rasa_x_utils.bool_arg(
            request, "exclude_leading_action_session_start", False)

        tracker = event_service.get_tracker_with_message_flags(
            conversation_id,
            until_time,
            since_time,
            event_verbosity,
            rasa_environment_query,
            exclude_leading_action_session_start,
        )

        if not tracker:
            return rasa_x_utils.error(
                HTTPStatus.NOT_FOUND,
                "ClientNotFound",
                f"Client for conversation_id '{conversation_id}' could not be found",
            )

        requested_format = request.headers.get("Accept")

        if requested_format == "application/json":
            dispo = f"attachment;filename={conversation_id}-dump.json"
            return response.json(
                tracker,
                content_type="application/json",
                headers={"Content-Disposition": dispo},
            )
        elif requested_format == "text/markdown":
            _events = events.deserialise_events(tracker["events"])
            story = Story.from_events(_events)
            exported = story.as_story_string(flat=True)
            return response.text(
                exported,
                content_type="text/markdown",
                headers={
                    "Content-Disposition":
                    f"attachment;filename={conversation_id}-story.md"
                },
            )
        else:
            return response.json(tracker,
                                 headers={"Content-Disposition": "inline"})
    async def change_password(request: Request) -> HTTPResponse:
        rjs = request.json
        user_service = UserService(request[REQUEST_DB_SESSION_KEY])

        try:
            user = user_service.change_password(rjs)
            if user is None:
                return rasa_x_utils.error(404, "UserNotFound",
                                          "user not found")
            return response.json(user)
        except MismatchedPasswordsException:
            return rasa_x_utils.error(403, "WrongPassword", "wrong password")
Exemple #11
0
 async def save_model_config(request, project_id, user=None):
     settings_service = SettingsService(request[REQUEST_DB_SESSION_KEY])
     try:
         config_yaml = settings_service.inspect_and_save_yaml_config_from_request(
             request.body, user["team"], project_id)
         return response.text(config_yaml)
     except YAMLError as e:
         return rasa_x_utils.error(
             400, "InvalidConfig",
             f"Failed to read configuration file.  Error: {e}")
     except InvalidConfigError as e:
         return rasa_x_utils.error(http.HTTPStatus.UNPROCESSABLE_ENTITY,
                                   "ConfigMissingKeys", f"Error: {e}")
Exemple #12
0
    async def train_model(request, project_id):
        stack_services = SettingsService(
            request[REQUEST_DB_SESSION_KEY]).stack_services(project_id)
        environment = utils.deployment_environment_from_request(
            request, "worker")
        stack_service = stack_services[environment]

        try:
            training_start = time.time()
            content = await stack_service.start_training_process()

            telemetry.track(telemetry.MODEL_TRAINED_EVENT)

            model_name = await _model_service(request).save_trained_model(
                project_id, content)

            nlg_service = NlgService(request[REQUEST_DB_SESSION_KEY])
            nlg_service.mark_responses_as_used(training_start)

            return response.json({
                "info": "New model trained.",
                "model": model_name
            })
        except FileExistsError as e:
            logger.error(e)
            return response.json(
                {
                    "info": "Model already exists.",
                    "path": str(e)
                },
                http.HTTPStatus.CREATED,
            )
        except ValueError as e:
            logger.error(e)
            return rasa_x_utils.error(
                http.HTTPStatus.BAD_REQUEST,
                "StackTrainingFailed",
                "Unable to train on data containing retrieval intents.",
                details=e,
            )
        except ClientError as e:
            logger.error(e)
            return rasa_x_utils.error(
                http.HTTPStatus.INTERNAL_SERVER_ERROR,
                "StackTrainingFailed",
                "Failed to train a Rasa model.",
                details=e,
            )
Exemple #13
0
    async def update_intent(request, intent_id, project_id):
        intent = request.json

        intent_service = _intent_service(request)
        existing_intents = intent_service.get_permanent_intents(project_id)
        if intent[INTENT_NAME_KEY] not in existing_intents:
            intent_service.update_temporary_intent(intent_id, intent,
                                                   project_id)

        user_goal = intent.get(INTENT_USER_GOAL_KEY)
        user_goal_service = _user_goal_service(request)
        try:
            if user_goal:
                user_goal_service.add_intent_to_user_goal(
                    user_goal, intent[INTENT_NAME_KEY], project_id)
            elif INTENT_USER_GOAL_KEY in intent.keys():
                user_goal_service.remove_intent_from_user_goal(
                    user_goal, intent[INTENT_NAME_KEY], project_id)
        except ValueError as e:
            logger.error(e)
            return error(400,
                         "UpdateIntentFailed",
                         "Failed to update user goal.",
                         details=e)
        return response.text(f"Intent '{intent_id}' updated.", 200)
Exemple #14
0
    async def add_conversation_tags(
        request: Request, conversation_id: Text
    ) -> HTTPResponse:
        """Assign tags to <conversation_id>. If tags don't exist, they will be created.

        Args:
            request: Incoming HTTP request.
            conversation_id: Id of conversation to assign tags to.

        Returns:
            List of assigned tags in format of JSON schema `conversation_tags`.
        """

        try:
            tags = _event_service(request).add_conversation_tags(
                conversation_id, request.json
            )

            telemetry.track_conversation_tagged(len(tags))

            return response.json(tags, headers={"X-Total-Count": len(tags)})
        except ValueError as e:
            return rasa_x_utils.error(
                HTTPStatus.BAD_REQUEST, "Failed to add tags to conversation", str(e)
            )
    async def update_user(
            request: Request,
            username: Text,
            user: Optional[Dict[Text, Any]] = None) -> HTTPResponse:
        """Update properties of a `User`."""

        if username != user["username"]:
            return rasa_x_utils.error(
                403, "UserUpdateError",
                "Users can only update their own propeties.")

        try:
            _user_service(request).update_user(user["username"], request.json)
            return response.text("", 204)
        except UserException as e:
            return rasa_x_utils.error(404, "UserUpdateError", details=e)
    async def create_conversation(request: Request,
                                  user: Dict[Text,
                                             Any] = None) -> HTTPResponse:
        payload = request.json or {}
        conversation_id = payload.get("sender_id")
        conversation_id_to_copy_from = payload.get(
            "conversation_id_to_copy_from")
        events_until = payload.get("until") or time.time()

        try:
            event_service = _event_service(request)
            conversation = event_service.create_new_conversation(
                conversation_id,
                interactive=True,
                created_by=user.get(USERNAME_KEY))
            # Commit changes right away to make sure the conversation is not created
            # by Rasa Open Source sending the events to the `EventService`
            event_service.commit()

            stack_service = _stack_service(request,
                                           RASA_PRODUCTION_ENVIRONMENT)

            if conversation_id_to_copy_from:
                _events = event_service.copy_events_from_conversation(
                    conversation_id_to_copy_from, events_until)
            else:
                initial_tracker = await stack_service.tracker_json(
                    conversation.sender_id)
                _events = initial_tracker["events"]

            tracker = await stack_service.append_events_to_tracker(
                conversation.sender_id, _events)

            return response.json(tracker, HTTPStatus.CREATED)
        except ClientError as e:
            return rasa_x_utils.error(
                HTTPStatus.UNPROCESSABLE_ENTITY,
                "ConversationCreationFailed",
                "Error while trying to create conversation.",
                details=e,
            )
        except ValueError as e:
            return rasa_x_utils.error(HTTPStatus.NOT_FOUND,
                                      "ConversationEventCopyError",
                                      details=e)
 async def delete_story(request: Request, story_id: Text) -> HTTPResponse:
     deleted = _story_service(request).delete_story(story_id)
     if deleted:
         return response.text("", HTTPStatus.NO_CONTENT)
     return rasa_x_utils.error(
         HTTPStatus.NOT_FOUND,
         "StoryNotFound",
         f"Failed to delete story with story with id '{story_id}'.",
     )
    async def get_domain(request: Request) -> HTTPResponse:
        domain_service = DomainService(request[REQUEST_DB_SESSION_KEY])
        domain = domain_service.get_domain()
        if domain is None:
            return rasa_x_utils.error(400, "DomainNotFound",
                                      "Could not find domain.")

        domain_service.remove_domain_edited_states(domain)
        return response.text(domain_service.dump_cleaned_domain_yaml(domain))
    async def get_domain_warnings(request: Request) -> HTTPResponse:
        domain_service = DomainService(request[REQUEST_DB_SESSION_KEY])
        domain_warnings = await domain_service.get_domain_warnings()
        if domain_warnings is None:
            return rasa_x_utils.error(400, "DomainNotFound",
                                      "Could not find domain.")

        return response.json(domain_warnings[0],
                             headers={"X-Total-Count": domain_warnings[1]})
Exemple #20
0
    async def delete_model(request, project_id, model):
        deleted = _model_service(request).delete_model(project_id, model)
        if deleted:
            return response.text("", http.HTTPStatus.NO_CONTENT)

        return rasa_x_utils.error(
            http.HTTPStatus.NOT_FOUND,
            "ModelDeleteFailed",
            f"Failed to delete model '{model}'.",
        )
    async def delete_user(request: Request, username: Text,
                          user: Dict) -> HTTPResponse:
        user_service = UserService(request[REQUEST_DB_SESSION_KEY])

        try:
            deleted = user_service.delete_user(
                username, requesting_user=user["username"])
            return response.json(deleted)
        except UserException as e:
            return rasa_x_utils.error(404, "UserDeletionError", e)
Exemple #22
0
 async def delete_response(request: Request,
                           response_id: int) -> HTTPResponse:
     deleted = _nlg_service(request).delete_response(response_id)
     if deleted:
         background_dump_service.add_domain_change()
         return response.text("", http.HTTPStatus.NO_CONTENT)
     return rasa_x_utils.error(
         http.HTTPStatus.NOT_FOUND,
         "ResponseNotFound",
         "Response could not be found.",
     )
 async def create_new_action(request: Request,
                             project_id: Text) -> HTTPResponse:
     domain_service = DomainService(request[REQUEST_DB_SESSION_KEY])
     try:
         created = domain_service.add_new_action(request.json, project_id)
         return response.json(created, status=201)
     except ValueError as e:
         return rasa_x_utils.error(400,
                                   "ActionCreationError",
                                   "Action already exists.",
                                   details=e)
Exemple #24
0
    async def get_model_config(request, project_id, user=None):
        settings_service = SettingsService(request[REQUEST_DB_SESSION_KEY])
        stack_config = settings_service.get_config(user["team"], project_id)
        if not stack_config:
            return rasa_x_utils.error(http.HTTPStatus.NOT_FOUND,
                                      "SettingsFailed",
                                      "Could not find settings.")

        yaml_config = rasa_x_utils.dump_yaml(stack_config)

        return response.text(yaml_config)
Exemple #25
0
 async def untag(request, project_id, model, tag):
     try:
         _model_service(request).delete_tag(project_id, model, tag)
         return response.text("", http.HTTPStatus.NO_CONTENT)
     except ValueError as e:
         return rasa_x_utils.error(
             http.HTTPStatus.NOT_FOUND,
             "TagDeletionFailed",
             "Failed to delete model tag",
             details=e,
         )
    async def issue_signed_jwt(request):
        id_object = request.json
        chat_token = _domain_service(request).get_token()
        if not chat_token:
            return rasa_x_utils.error(
                HTTPStatus.NOT_FOUND,
                "ChatTokenNotFound",
                "Could not find valid chat_token.",
            )
        if id_object["chat_token"] != chat_token["chat_token"]:
            return rasa_x_utils.error(HTTPStatus.UNAUTHORIZED,
                                      "InvalidChatToken",
                                      "Chat token is not valid.")

        if _domain_service(request).has_token_expired(id_object["chat_token"]):
            return rasa_x_utils.error(HTTPStatus.UNAUTHORIZED,
                                      "ChatTokenExpired",
                                      "Chat token has expired.")

        # set JWT expiration to one day from current time or chat token
        # expiration time, whichever is lower
        expires = min(int(time.time()) + 24 * 60 * 60, chat_token["expires"])
        conversation_id = uuid.uuid4().hex

        jwt_object = {
            "username": conversation_id,
            "exp": expires,
            "user": {
                "username": conversation_id,
                "roles": [user_service.GUEST],
            },
            "scopes": normalise_permissions(guest_permissions()),
        }

        encoded = rasax.community.jwt.encode_jwt(jwt_object,
                                                 config.jwt_private_key)

        return response.json({
            "access_token": encoded,
            "conversation_id": conversation_id
        })
Exemple #27
0
    async def upload_model(request, project_id):
        model_service = _model_service(request)
        try:
            tpath = model_service.save_model_to_disk(request)
        except (FileNotFoundError, ValueError, TypeError) as e:
            return rasa_x_utils.error(
                http.HTTPStatus.BAD_REQUEST,
                "ModelSaveError",
                f"Could not save model.\n{e}",
            )

        minimum_version = await model_service.minimum_compatible_version()

        if not model_service.is_model_compatible(minimum_version, fpath=tpath):
            return rasa_x_utils.error(
                http.HTTPStatus.BAD_REQUEST,
                "ModelVersionError",
                f"Model version unsupported.\n"
                f"The minimum compatible version is {minimum_version}.",
            )

        try:
            filename: Text = os.path.basename(tpath)
            model_name = filename.split(".tar.gz")[0]
            saved = await model_service.save_uploaded_model(
                project_id, model_name, tpath)
            if saved:
                telemetry.track(telemetry.MODEL_UPLOADED_EVENT)
                return response.json(saved, http.HTTPStatus.CREATED)
            return rasa_x_utils.error(
                http.HTTPStatus.BAD_REQUEST,
                "ModelSaveError",
                "Could not save model.\nModel name '{}'."
                "File path '{}'.".format(model_name, tpath),
            )
        except FileExistsError:
            return rasa_x_utils.error(
                http.HTTPStatus.CONFLICT,
                "ModelExistsError",
                "A model with that name already exists.",
            )
 async def delete_message_correction(
         request: Request, conversation_id: Text,
         message_timestamp: float) -> HTTPResponse:
     try:
         project_id = rasa_x_utils.default_arg(request, "project_id",
                                               config.project_name)
         _event_service(request).delete_message_correction(
             conversation_id, message_timestamp, project_id)
         return response.text("", HTTPStatus.OK)
     except ValueError as e:
         return rasa_x_utils.error(HTTPStatus.BAD_REQUEST,
                                   "MessageRelabellingError.", e)
    async def get_runtime_config(_: Request) -> HTTPResponse:
        config_dict, errors = config_service.get_runtime_config_and_errors()

        if errors:
            return rasa_x_utils.error(
                400,
                "FileNotFoundError",
                rasa_x_utils.add_plural_suffix(
                    "Could not find runtime config file{}.", errors),
                details=errors,
            )

        return response.json(config_dict)
    async def get_messages(request, conversation_id, user=None):
        event_service = _event_service(request)

        if not _has_access_to_conversation(event_service, conversation_id,
                                           user):
            return rasa_x_utils.error(HTTPStatus.UNAUTHORIZED, "NoPermission",
                                      "Access denied")

        until_time = rasa_x_utils.float_arg(request, "until", None)
        event_verbosity = _event_verbosity_from_request(request)
        tracker = event_service.get_messages_for(conversation_id, until_time,
                                                 event_verbosity)

        if not tracker:
            return rasa_x_utils.error(
                HTTPStatus.NOT_FOUND,
                "ClientNotFound",
                f"Client for conversation_id '{conversation_id}' could not be found.",
            )

        return response.json(
            tracker, headers={"X-Total-Count": len(tracker.get("messages"))})