How to pass metadata in POST request and then extract it?

Hi, I want to pass some extra data in POST request. In Custom Connectors it is said that:

If you need to use extra information from your front end in your custom actions, you can add this information in the metadata dict of your user message. This information will accompany the user message through the rasa server into the action server when applicable, where you can find it stored in the tracker .

Basic json looks like this:

{
  "sender": "123",
  "message": "Hello"
}

How should I append it with metadata?

Also how should I extract metadata from tracker in my custom action?

@amn41 @erohmensing @Juste @alexweidauer @Ghostvv @Tanja @j.mosig @ricwo @Tobias_Wochinger

Does anyone know the answer?

1 Like

Not 100% what your plan is. Do you want to augment the NLU data? In any way, the output of the NLU pipeline is pretty much a dictionary (which is transferred as a json). Use a custom component to include additional information to that output dictionary.

You’ll find the output dictionary of the NLU pipeline in tracker.latest_message, where you can easily access it.

Ok, this seems to be a pretty new feature. Did some research and found tracker now got a ‘get_latest_input_channel()’ method. Guess, you should look into that one to find the metadata

Hi @IgNoRaNt23, thanks for response, my goal is to pass to chatbot JSON like this

{
	"sender": "123",
	"message": "hey",
	"metadata":"additional info"
} 

and then in my custom action extract and assign this additional info into a slot

get_latest_input_channel() returns the name of last UserUttered event so for me it just returns rest. Do you have any other idea how to get this data?

Any hint, what kind of additional information you want to pass?

Anyway, didnt even know metadata existed before I looked into the most recent code. So I guess, you look what happens to the metadata dictionary in the UserMessage-Class. As the docs say, it ends up on tracker somehow. Or just print out everything you got on tracker and see if you can find it.

Okay, I finally did it

JSON:

{
	"sender": "12321133",
	"message": "hey",
	"metadata": {"aaaa":"AAAAAAA", "bbbbb":"BBBBBB"}  	
}

It’s necessary to implement custom channel -> Custom Connectors

In RestInput class create method _extract_metadata

def _extract_metadata(self, req: Request) -> Text:
    return req.json.get("metadata") or self.name()

Extract metadata in receive method

@custom_webhook.route("/webhook", methods=["POST"])
async def receive(request: Request):
    sender_id = await self._extract_sender(request)
    text = self._extract_message(request)
    metadata = self._extract_metadata(request)
    metadata = "{\"metadata\": \"" + str(metadata) + "\"}"
    metadata = json.loads(metadata)

Few lines below pass extracted metadata into UserMessage constructor

try:
    await on_new_message(
        UserMessage(
            text, collector, sender_id, input_channel=input_channel, metadata=metadata
        )
    )

Then you may extract metadata from last user message in custom action like this (not a perfect code but it works)

events = tracker.current_state()['events']
user_events = []
for e in events:
    if e['event'] == 'user':
        user_events.append(e)

print(user_events[-1]['metadata'])

Action endpoint server output:

{'metadata': "{'aaaa': 'AAAAAAA', 'bbbbb': 'BBBBBB'}"}
127.0.0.1 - - [2019-09-02 13:42:41] "POST /webhook HTTP/1.1" 200 169 0.003994
8 Likes

Hey @kaladin, awesome that you’ve worked the rest input to take in metadata. We already have an issue open for this: Metadata field should be passed through the input channel · Issue #4295 · RasaHQ/rasa · GitHub, would you like to open source your changes so that the REST channel takes metadata by default? Seems like lots of people would be interested in your changes :slight_smile:

P.S. thanks for the descriptive answer of how you accomplished it!

2 Likes

hi, thanks for your answer. And I want to know, how to replace the restChannel by your code?

Hi @kukuxiahuni,

if you follow these instructions you will notice that the code has been embedded natively. The only thing to mention is that you have to overwrite the get_metadata method of your CustomConnector and you have to e.g. use this one to extract it from any Action:

@staticmethod
def extract_metadata_from_tracker(tracker: Tracker):
    events = tracker.current_state()['events']
    user_events = []
    for e in events:
        if e['event'] == 'user':
            user_events.append(e)

    return user_events[-1]['metadata']

Keep in mind that there is no validation - it’s just quick and dirty.

Kind regards
Julian

3 Likes

Is there a way to get access to metadata without implementing you own custom connectors? The docs says " This information will accompany the user message through the rasa server into the action server when applicable, where you can find it stored in the tracker" So how do I get access to metadata on tracker without implementing my own custom connectors?

Hi @wisdom_kwarteng,

since Rasa won’t know what you are sending as metadata, they simply implemented:

def get_metadata(self, request: Request) -> Optional[Dict[Text, Any]]:
    """Extracts additional information from the incoming request.

     Implementing this function is not required. However, it can be used to extract
     metadata from the request. The return value is passed on to the
     ``UserMessage`` object and stored in the conversation tracker.

    Args:
        request: incoming request with the message of the user

    Returns:
        Metadata which was extracted from the request.
    """
    pass

for e.g. in the RestInputConnector such that you are free to implement it on your own - without a new CustomConnector.

Kind regards
Julian

@JulianGerhard Thank you

Has anyone been able to successfully get metadata in action_session_start?

I can get the metadata in other actions but it looks like action_session_start runs before any messages are processed. It would be great to extract a user’s name from the additional metadata in action_session_start so we can continue to use the Mapping policy for greet intent without having to create a custom action for that.

1 Like

I am not sure if this question goes here. For our project, when a user clicks a link we are opening a chatbot window where it will collect additional details. To that page, we will pass some query parameters such as user information. Now, what we want to do is getting the query parameters from the URL in front-end and send it to Rasa so that it can store them in slots. We will use those slots for later purposes.

Hello @kaladin

here Rasa Webchat Integration you will find everything you need to do it (it’s long, but every reply there is essential).

I event did a improvement on RASA and integrated a PR with it.

Hope this helps.

Cya!

@kaladin May i ask you where to find the class RestInput?

@jhzape You can find it here, at the bottom: Custom Connectors

1 Like

Is there a cleaner way to extract the metadata without running a for loop to extract it? I mean the use case should be general enough to warrant a dedicated way to access it right?

Hey @wakandan,

You can extract using the following line

metadata = tracker.latest_message.get("metadata")

2 Likes

Added 2 things
 One to my custom channel connector → inherited from input channel

custom_channel.py

def get_metadata(self, req: Request) -> Optional[Dict[Text, Any]]:
        """Extracts the metadata from a user message.
        Args:
            request: A `Request` object
        Returns:
            Metadata extracted from the sent event payload.
        """
        return req.json.get("metadata", None)

in custom actions inside any action

action.py

metadata = tracker.latest_message.get("metadata")
1 Like