Why do slots of type list not return singleton lists?

Suppose I have a slot of type list, and within a form, the slot is defined to extract entity values. In such cases, I have noticed that if a single entity value is extracted, the slot returns not a list containing that element, but rather the element itself. This is frustrating, since in my validation action where I validate each entity extracted, I need to separately consider the cases where a single vs. multiple entities were extracted.

I’m curious to know if this is a bug, or if there is a reason for why the list slot is designed like this.

Hey @alexyuwen, thanks for the very interesting question!

I tried this out, defined a slot of type list which gets populated in my form from two different entities: day and colour:

forms:
  my_form:
    daycolour:
    - type: from_entity
      entity: day
    - type: from_entity
      entity: colour

Then, when the form runs:

  1. if I utter something that contains only 1 instance of an entity, the slot gets filled but becomes just that entity (not a list containing the entity) – as you described
  2. if I utter something with 2 instances of the same entity, both extracted entities end up in the slot and it becomes a list
  3. when my utterance contains 1 instance of each of the 2 entities (i.e. 1x day and 1x colour), the slot surprisingly doesn’t become a list! Instead, the slot becomes just the entity that’s used first in the slot mapping definition in the domain – i.e. it becomes just the day entity

Clearly, this behaviour is inconsistent. @alexyuwen I suggest you create a bug report on GitHub (feel free to tag me – @samsucik – in it). First, however: What’s the ideal behaviour you imagine? Do you think the slot should always be a list, containing all entities that are mapped to the slot? (I.e. containing both the day and colour entities in my 3rd scenario.) I’m curious what you think.

(More food for thought: What do you think should happen when the slot is not a list, but an utterance contains multiple entities that are mapped to the slot? Rasa deals with this in a particular way but I’m interested in your opinion :wink: )

I had trouble with lists as well. I would assume that if I declare the slot type, it keeps this type. The only exception is the “None” case.

For the specific problem above, I argue that a list contains instances of one entity only. This should simplify validations and other cases where one needs to iterate a list slot.

In my application cases, I found that I miss something like a “complex” slot type. A slot that accepts mixed entities (day, colour) should be a list where each element is a dictionary.

[ {“colour”: “a”, “day”: “x”}, {“colour”: “b”, “day”: “y”} ]

The list behaviour helps accessing related entities the user provided, e.g. a day and a color. While the dictionary allows me to retrieve one of the values using semantics (“colour” or “day”) instead of list indexes. The latter is useful when I build my response text.

Hey @ScienceGuy, thanks for the input :slight_smile:

For the specific problem above, I argue that a list contains instances of one entity only.

Okay, but… Would you raise an error if a list slot is mapped to different entities in the domain? What about when it’s mapped to an entity and also to an intent (using from_intent)? To me, it’s all getting quite hairy at this point :wink:

A slot that accepts mixed entities (day, colour) should be a list where each element is a dictionary. [ {“colour”: “a”, “day”: “x”}, {“colour”: “b”, “day”: “y”} ]

Do I understand correctly that each dict in there comes from one user utterance? Then you’d only achieve this kind of slot contents if you filled it from some custom action (Rasa itself wouldn’t add a new entry to a list slot I think – instead, it would replace the entire slot value with the new entry :thinking: ). If we forget about this detail, I think we can talk about a slot that would be mapped from different entities and stored as a dict, each entity having a key:value record in the dict:

{"colour": "a", "day": "x"}

Indeed, the entire dict would get overwritten completely every time (unless you’d tweak the slot filling in a custom action). Now, what I’m struggling with is the complexity here: Why would you want to have a complex slot type for this, instead of having 2 slots for the different entities? If you really want, in a custom action, you can still grab those 2 slots and combine them into a complex one, or even create a list of dicts that accumulates new entries over time, but I think that creating a dedicated slot type wouldn’t necessarily help much. Sorry if this sounds discouraging, I just think that once you get into advanced use cases, there starts to be too many of them because of minute specific, with no one being too common – and then the question is whether it’s not better after all to let users implement these in their custom actions instead of providing support for tens of very specific use cases. This being said, this forum is great for collecting evidence on how popular specific patterns or use cases are, and of course it makes sense to add something to Rasa if many folks use it :slight_smile:

Hey, thanks for the quick reply.

Okay, but… Would you raise an error if a list slot is mapped to different entities in the domain? What about when it’s mapped to an entity and also to an intent (using from_intent)? To me, it’s all getting quite hairy at this point

I argue that a “list” contains things of the same type. If you squeeze other things in it, e.g. a date into a color list I would like to be warned as a developer.

Do I understand correctly that each dict in there comes from one user utterance? Then you’d only achieve this kind of slot contents if you filled it from some custom action (Rasa itself wouldn’t add a new entry to a list slot I think – instead, it would replace the entire slot value with the new entry :thinking: ). If we forget about this detail, I think we can talk about a slot that would be mapped from different entities and stored as a dict, each entity having a key:value record in the dict:

The mixed entities example indeed asumes that the user provides all entities in one utterance.

Indeed, the entire dict would get overwritten completely every time (unless you’d tweak the slot filling in a custom action). Now, what I’m struggling with is the complexity here: Why would you want to have a complex slot type for this, instead of having 2 slots for the different entities? If you really want, in a custom action, you can still grab those 2 slots and combine them into a complex one, or even create a list of dicts that accumulates new entries over time, but I think that creating a dedicated slot type wouldn’t necessarily help much. Sorry if this sounds discouraging, I just think that once you get into advanced use cases, there starts to be too many of them because of minute specific, with no one being too common – and then the question is whether it’s not better after all to let users implement these in their custom actions instead of providing support for tens of very specific use cases. This being said, this forum is great for collecting evidence on how popular specific patterns or use cases are, and of course it makes sense to add something to Rasa if many folks use it

I agree that the complex slot does not make much sense if it is overwritten. I think it would be relevant when I need to keep the relationship between two entities intact for several turns. Relationship means that e.g. date and color belong to the same context. I try to make a different example:

User: "Put apples and pears on the shopping list for tomorrow"
Bot: shopping-list-slot: { "day": "tomorrow", "items": ["apples", "pears"] }

If I maintain two separate slots, I am ok as long as no entity apears in the conversation that overrides one of the slots but not the other. I am not asking for a “complex” slot in particular, but I felt that the slot mechanics in Rasa make it quite difficult to handle complex context where entities relate to each other and I need to navigate between them - but maybe this is not even the scope of the slots anymore but a data organization issue of the backend (custom actions). While talking about that, are there plans to introduce alternative tools to manage complex contexts in Rasa?

To @SamS’s original reply, I think that, as you said, the slot should always be a list, containing all entities that are mapped to the slot, not differentiating between the specific entities they were extracted from. The reason is, from the developer’s perspective, it seems like a good design choice to allow the slot to hold only one type of information. So, for example, I might define a slot weekly_frequency to extract both number and frequency entities, where the frequency entity is trained to recognize “once” as 1 and “daily” as 7. In such a case, I care only about the values extracted, not the specific entities they were extracted from. But in the daycolour example you gave, the two entities seem different enough that 1.) if you expected the user to provide values for both entities, and 2.) if you cared about the actual values (and were to make use of them), then you should probably use two different slots.

Also, I ran some tests and noticed that if the slot is not of type list, but an utterance contains multiple entities that are mapped to the slot, the slot returns a list anyway?! For some reason, I had always thought that the slot would only be filled with the first instance of the entity. This seems inconsistent; I’m surprised I haven’t run into problems with this behavior yet. Because if you expect multiple values, then you would just use a list slot. But if you expect a single value, you expect to be working with a string, not a list. If you wanted to check for multiple values, that’s easy enough to do using the tracker.

So to summarize, if I define the slot to be a list type, then I expect to work with a list. The only exception would be if no entities were extracted, in which case a null value seems consistent.

1 Like

@ScienceGuy I agree that storing related values together in a bundle is useful. On the entity level, grouping things is already supported with entity groups and roles, but this isn’t currently reflected in the different slot types. On the other hand, I think it’d be hard to come up with one approach that fits all use cases, so perhaps it’s better to leave it as something that can be achieved with custom actions by using an any slot or even a custom slot type? @erohmensing @tyd what do you think?

Also, I ran some tests and noticed that if the slot is not of type list, but an utterance contains multiple entities that are mapped to the slot, the slot returns a list anyway?! For some reason, I had always thought that the slot would only be filled with the first instance of the entity.

@alexyuwen the slot does get filled with only the first instance of each entity, but if you have multiple entities mapped to the slot, then the slot just receives multiple extracted entities. I agree that this is somewhat inconsistent with the situation where you have multiple instances of the same entity type and just the first one fills the slot… But would the suggested behaviour be always desirable and better than the current one? What are your thoughts? @erohmensing I’m curious to hear from you too :slight_smile: One thing is for sure, this should be explicitly covered in the docs.

1 Like

@SamS I prefer the general solution of keeping it as something that can be achieved with custom actions by using an any slot or even a custom slot type, but I am open to hearing what advantages exposing it with a more specific abstraction would give Rasa Open Source users

Just an update: I’m currently using the following helper method in every one of my validation methods for slots of type list.

def list_slot_to_list(slot):
    if slot is None:
        return []
    elif isinstance(slot, str):
        return [slot]
    else:
        return slot.copy()