Extract "from" and "to" values from duckling time-entity

Is it possible to extract the “from” and “to” sub-values from the duckling time-entity, into different spots?

Duckling recognizes the values for “form” and “to” as sub-values for the time-entity correctly, but how to get these sub-values and put them into different slots.

Thanks in advance.

Hi Johannes,

Could you give an example with what you mean with from and to?

If you’re referring to composite entities, these are not supported by Duckling.

Hi,

The response from duckling looks in rasa x like this:

apply_for_vacation{“time”:{“to”:“2020-06-10T00:00:00.000+02:00”,“from”:“2020-06-08T00:00:00.000+02:00”}, …}

Now I would like to extract the values “from” and “to” in 2 different slots. The idea behind this is that I want to store the data in a form so that any missing data can be explicitly queried again.

I would like to have an answer for this as well. I am struggling to get the unit of a volume extracted with Duckling. The slot only contains the value but not the unit of it. Is this an example for a composite entity or am I missing another point?

Oh yes! I have the same difficulty with the Duckling-extracted entity “duration” and I’m stuck. The get_slot / slot_value only returns an integer, for instance a “3”, but doesn’t return the unit, so regardless of whether the user said “3 minutes” or “3 hours”, it will only display “3”.

In the debug mode, however, I can see the extracted entity parameters in their entirety:

2020-11-13 14:32:25 DEBUG rasa.core.actions.forms - Validating user input ‘UserUttered(text: 2 Stunden, intent: {‘id’: -8607876346569641186, ‘name’: ‘say_q3_number_of_participants’, ‘confidence’: 0.8645510077476501}, entities: [{‘start’: 0, ‘end’: 9, ‘text’: ‘2 Stunden’, ‘value’: 2, ‘confidence’: 1.0, ‘additional_info’: {‘value’: 2, ‘hour’: 2, ‘type’: ‘value’, ‘unit’: ‘hour’, ‘normalized’: {‘value’: 7200, ‘unit’: ‘second’}}, ‘entity’: ‘duration’, ‘extractor’: ‘DucklingEntityExtractor’}])’.

But which command will allow me to access these parameters in a custom action? I would like to extract the “normalized” value in seconds: ‘value’: 7200, ‘unit’: ‘second’, or at least the original number and the unit, in order to be able to convert these data into a timedelta object?

TIA!

Edit: Solved! I’ve found out the solution to extract nested values from the Duckling-extracted entities in a custom action. For instance, to extract the duration unit from the last utterance of the user, one needs to use this code:

duration_unit = tracker.latest_message[‘entities’][0][‘additional_info’][‘unit’]

I am having the same problem. Where in the custom actions would I implement this solution that you provided. TIA!

@_daisy In my case, I used this code in the form validation action to extract properties of Duckling “duration” entity from “q5_duration” slot in order to set a custom slot, “q6_ending_time”:

from typing import Dict, Text, Any, List
from rasa_sdk import Action, Tracker, FormValidationAction
from rasa_sdk.events import SlotSet, UserUtteranceReverted, EventType
from rasa_sdk.executor import CollectingDispatcher
from rasa_sdk.types import DomainDict

from datetime import datetime, timedelta

class ValidateRoomReservationForm(FormValidationAction):
    def name(self) -> Text:
        return "validate_room_reservation_form"

    def validate_q1_location(
        self,
        slot_value: Any,
        dispatcher: CollectingDispatcher,
        tracker: Tracker,
        domain: DomainDict,
    ) -> Dict[Text, Any]:
        """Validate location value."""

        (...)

    def validate_q2_date(
        self,
        slot_value: Any,
        dispatcher: CollectingDispatcher,
        tracker: Tracker,
        domain: DomainDict,
    ) -> Dict[Text, Any]:
        """Validate date value."""

        (...)

    def validate_q3_number_of_participants(
        self,
        slot_value: Any,
        dispatcher: CollectingDispatcher,
        tracker: Tracker,
        domain: DomainDict,
    ) -> Dict[Text, Any]:
        """Validate number of participants value."""

        (...)

    def validate_q4_starting_time(
        self,
        slot_value: Any,
        dispatcher: CollectingDispatcher,
        tracker: Tracker,
        domain: DomainDict,
    ) -> Dict[Text, Any]:
        """Validate starting time value."""
        
        (...)

    def validate_q5_duration(
        self,
        slot_value: Any,
        dispatcher: CollectingDispatcher,
        tracker: Tracker,
        domain: DomainDict,
    ) -> Dict[Text, Any]:
        """Validate starting time value."""
        
        starting_time = tracker.get_slot('q4_starting_time')
        starting_time_converted = datetime.fromisoformat(starting_time)
        duration_unit = tracker.latest_message['entities'][0]['additional_info']['unit']
        if duration_unit == 'second':
            duration_in_seconds = slot_value
        elif duration_unit == 'minute':
            duration_in_seconds = slot_value * 60
        elif duration_unit == 'hour':
            duration_in_seconds = slot_value * 3600
        
        ending_time = starting_time_converted + timedelta(seconds = duration_in_seconds)
        
        return{"q5_duration": slot_value, "q6_ending_time": ending_time.isoformat()}

This gives just the unit duration_unit = tracker.latest_message[‘entities’][0][‘additional_info’][‘unit’]

How would I get value and unit, if I may ask.

Thanks

With the same kind of syntax, just replace the ‘unit’ with ‘value’. You can extract it to another variable and/or concatenate the result. Or convert it into a timedelta.

You can just run Rasa in a debug mode and chat with it, provide a duration and make sure it has been extracted by Duckling, and then see the debug log right after you have provided the duration to see how exactly the properties you need are nested:

entities: [{‘start’: 0, ‘end’: 9, ‘text’: ‘2 Stunden’ , ‘value’: 2, ‘confidence’: 1.0, ‘additional_info’: {‘value’: 2, ‘hour’: 2, ‘type’: ‘value’, ‘unit’: ‘hour’, ‘normalized’: {‘value’: 7200, ‘unit’: ‘second’}}, ‘entity’: ‘duration’, ‘extractor’: ‘DucklingEntityExtractor’}]

Thanks. I was able to use this code;

data = tracker.latest_message[“entities”] data = list(filter( lambda entity: entity[‘entity’] == “duration”, tracker.latest_message[“entities”] ))

if data:

values = data[0][“additional_info”][“value”] unit = data[0][“additional_info”][“unit”] return {“slot_name”: f’{values} {unit}’}

else:

return {“slot_name”: None}

Cool! Great.