Custom graph component

Hi, i am using Rasa 3 but i didn’t manage to implement a sentiment analysis vader sentiment . I didn’t understand how it works … Is it possible to have a tutorial détailed about it? How to use graph component. How to find the way to put from something import SentimentAnalyzer ? Thank you for all your effort for rasa and i hope i will quickly understand all of this ^^

Hello @Hajer and welcome to the forum!

You can start with this blog post: How to Enhance Rasa NLU Models with Custom Components | Rasa Blog | The Rasa Blog | Rasa hope this will give you a better idea.

Thank you Nik202 but the version of rasa is 2 not 3. I already read it.

@Hajer Hmmm…but try to implement and see the results, coding will be the same?

1 Like

Graph component is here to connect a new node with other components. In graphcomponent class, i put : from nltk.sentiment.vader import SentimentIntensityAnalyzer In sentiment.py i don’t know how to call SentimentIntensityAnalyzer… do i have to put from rasa.models import SentimentIntensityAnalyzer ?? but it doesn’t work … I don’t understand this part.

share the complete code?

import nltk

from nltk.sentiment.vader import SentimentIntensityAnalyzer

OR

pip install vaderSentiment

from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer

Yes okay

from __future__ import annotations
from typing import Dict, Text, Any
from rasa.engine.graph import GraphComponent, ExecutionContext
from rasa.engine.storage.resource import Resource
from rasa.engine.storage.storage import ModelStorage
#from rasa.models import SentimentIntensityAnalyzer
import os
from pathlib import Path
from rasa.shared.nlu.training_data.training_data import TrainingData
from rasa.engine.recipes.default_recipe import DefaultV1Recipe
import json

@DefaultV1Recipe.register(
	DefaultV1Recipe.ComponentType.MODEL_LOADER, is_trainable=True
)
class SentimentAnalyzer(GraphComponent, SentimentIntensityAnalyzer):
	"""A pre-trained sentiment component"""

	name = "sentiment"
	provides = ["entities"]
	requires = []
	defaults = {}
	language_list = ["fr"]


	def __init__(self, component_config= Dict[Text, Any]) -> None:
		self.component_config = component_config
	@classmethod
	def create(
		cls,
		config: Dict[Text, Any],
		model_storage: ModelStorage,
		resource: Resource,
		execution_context: ExecutionContext,
	) -> GraphComponent:
		return cls(config)

	def train(self, training_data, cfg, **kwargs):
		"""Load the sentiment polarity labels from the text
		file, retrieve training tokens and after formatting
		data train the classifier."""
		pass
	def convert_to_rasa(self, value, confidence):
		"""Convert model output into the Rasa NLU compatible output format."""

		entity = {"value": value,
				  "confidence": confidence,
				  "entity": "sentiment",
				  "extractor": "sentiment_extractor"}

		return entity

	def process(self, message, **kwargs):
			"""Retrieve the text message, pass it to the classifier
				and append the prediction results to the message class."""

			sid = SentimentIntensityAnalyzer()
			res = sid.polarity_scores(message.text)
			key, value = max(res.items(), key=lambda x: x[1])

			entity = self.convert_to_rasa(key, value)

			message.set("entities", [entity], add_to_output=True)


	def persist(self, file_name, model_dir):
		"""Pass because a pre-trained model is already persisted"""

		pass
	@classmethod
	def load(
		cls,
		config: Dict[Text, Any],
		model_storage: ModelStorage,
		resource: Resource,
		execution_context: ExecutionContext,
		**kwargs: Any,
	) -> SentimentAnalyzer:
		model_data = {}

		try:
			with model_storage.read_from(resource) as path:

				model_data_file = path / "model_data.json"
				model_data = json.loads(rasa.shared.utils.io.read_file(model_data_file))

		except (ValueError, FileNotFoundError, FileIOException):
			logger.debug(
				f"Couldn't load metadata for component '{cls.__name__}' as the persisted "
				f"model data couldn't be loaded."
			)

		return cls(
			config, model_data=model_data
		)
from __future__ import annotations
import nltk
from nltk.sentiment.vader import SentimentIntensityAnalyzer
from typing import List, Type, Dict, Text, Any, Optional

from rasa.engine.graph import ExecutionContext
from rasa.engine.storage.resource import Resource
from rasa.engine.storage.storage import ModelStorage


class GraphComponent(SentimentIntensityAnalyzer):
    """Interface for any component which will run in a graph."""

    @classmethod
    def required_components(cls) -> List[Type]:
        """Components that should be included in the pipeline before this component."""
        return []

    @classmethod
    @abstractmethod
    def create(
        cls,
        config: Dict[Text, Any],
        model_storage: ModelStorage,
        resource: Resource,
        execution_context: ExecutionContext,
    ) -> GraphComponent:
        """Creates a new `GraphComponent`.
            config: This config overrides the `default_config`.
            model_storage: Storage which graph components can use to persist and load
                themselves.
            resource: Resource locator for this component which can be used to persist
                and load itself from the `model_storage`.
            execution_context: Information about the current graph run.

        Returns: An instantiated `GraphComponent`.
        """
        ...

    @classmethod
    def load(
        cls,
        config: Dict[Text, Any],
        model_storage: ModelStorage,
        resource: Resource,
        execution_context: ExecutionContext,
        **kwargs: Any,
    ) -> GraphComponent:
        """Creates a component using a persisted version of itself.

        If not overridden this method merely calls `create`.

        Args:
            config: The config for this graph component. This is the default config of
                the component merged with config specified by the user.
            model_storage: Storage which graph components can use to persist and load
                themselves.
            resource: Resource locator for this component which can be used to persist
                and load itself from the `model_storage`.
            execution_context: Information about the current graph run.
            kwargs: Output values from previous nodes might be passed in as `kwargs`.

        Returns:
            An instantiated, loaded `GraphComponent`.
        """
        return cls.create(config, model_storage, resource, execution_context)

    @staticmethod
    def get_default_config() -> Dict[Text, Any]:
        """Returns the component's default config.

        Default config and user config are merged by the `GraphNode` before the
        config is passed to the `create` and `load` method of the component.

        Returns:
            The default config of the component.
        """
        return {}

    @staticmethod
    def supported_languages() -> Optional[List[Text]]:
        """Determines which languages this component can work with.

        Returns: A list of supported languages, or `None` to signify all are supported.
        """
        return None

    @staticmethod
    def not_supported_languages() -> Optional[List[Text]]:
        """Determines which languages this component cannot work with.

        Returns: A list of not supported languages, or
            `None` to signify all are supported.
        """
        return None

    @staticmethod
    def required_packages() -> List[Text]:
        """Any extra python dependencies required for this component to run."""
        return []

@Hajer please check the above post of mine, and please format the code for me in a single one.

I dit import nltk from nltk.sentiment.vader import SentimentIntensityAnalyzer

I put the two code in one code. i hope it’s okay now. Thank you for your help

@Hajer can you check this thread and code used? Sentiment component - #20 by Fares

when i run my code, i got this message : GraphSchemaValidationException: Your model uses a graph component ‘SentimentAnalyzer’ which does not have the required method ‘provide’. Please make sure you’re either using the right component or that your component is registered with the correct component type.See Custom Graph Components for more information.

import nltk from nltk.sentiment.vader import SentimentIntensityAnalyzer these sentences i put them in graph component if i am not wrong.

I think it’s because i have to replace this : from rasa.models import SentimentIntensityAnalyzer by something else in sentiment.py

Yes, there can be a possibility of the issue checking the code and trying to implement the mentioned code on the blog first or thread I shared with you and then proceeding with others?

ImportError: Cannot retrieve class from path sentiment.SentimentIntensityAnalyzer. I have this error now :’(

I have a file named sentiment.py when i put a SentimentIntensityAnalyzer class inside. Normally it’s okay no?

Is your sentiment.py file is with action.py file in same folder or directory?

it wasn’t but when i put sentiment.py in action folder, it gives me the same error.

do check the code on sentiment.py try run the file using python first and then using rasa.

Thank you Nik202. I tried to do what you had tell to Fares. I run shell --debug and i have this in my screen : 021-12-21 16:21:36 DEBUG h5py._conv - Creating converter from 7 to 5 2021-12-21 16:21:36 DEBUG h5py._conv - Creating converter from 5 to 7 2021-12-21 16:21:36 DEBUG h5py._conv - Creating converter from 7 to 5 2021-12-21 16:21:36 DEBUG h5py._conv - Creating converter from 5 to 7 2021-12-21 16:21:38 INFO rasa_sdk.executor - Registered function for ‘action_conversation’. 2021-12-21 16:21:38 INFO rasa_sdk.endpoint - Action endpoint is up and running on http://0.0.0.0:5055 2021-12-21 16:21:38 DEBUG rasa_sdk.utils - Using the default number of Sanic workers (1).

Is it working ? And i am waiting to run actions --debug.

But all information will be display in the terminal ?

For information, i put sentiment.py and graph_compoenent in actions folder and i add the code action_conversation for dataframe in action.py.

see it’s saying action endpoint up and running on http://0.0.0.0:5055 and later debug you not shared, so yes well done at least you have run the code. Now try to your bot using rasa --shell or whatever is your use case. I already told you I have personally not implemented this use case, and me just trying to help you and motivate you.

1 Like

@nik202 thank you, i will replace the code of sentiment.py by the code of new cusom action. I think it will be more logical. because now it’s not working about displaying sentiment of text