## Usage

To use forms with Rasa you need to make sure that the [Rule Policy](https://legacy-docs-oss.rasa.com/docs/rasa/policies#rule-policy) is added to your policy configuration.
For example:

```yaml
policies:
  - name: RulePolicy
```

### Defining a Form

Define a form by adding it to the `forms` section in your [domain](https://legacy-docs-oss.rasa.com/docs/rasa/domain). The name of the form is also the name of the action which you can use in [stories](https://legacy-docs-oss.rasa.com/docs/rasa/stories) or [rules](https://legacy-docs-oss.rasa.com/docs/rasa/rules) to handle form executions. You will need to specify a list of slot names to the mandatory `required_slots` key.

The following example form `restaurant_form` will fill the slot `cuisine` and slot `num_people`.

```yaml
entities:
  - cuisine
  - number
slots:
  cuisine:
    type: text
    mappings:
      - type: from_entity
        entity: cuisine
  num_people:
    type: any
    mappings:
      - type: from_entity
        entity: number
forms:
  restaurant_form:
    required_slots:
      - cuisine
      - num_people
```

You can define a list of intents to ignore for the whole form under the `ignored_intents` key. Intents listed under `ignored_intents` will be added to the `not_intent` key of each slot mapping.

For example, if you do not want any of the required slots of a form to be filled when the intent is `chitchat`, then you would need to define the following:

```yaml
entities:
  - cuisine
  - number
slots:
  cuisine:
    type: text
    mappings:
      - type: from_entity
        entity: cuisine
  num_people:
    type: any
    mappings:
      - type: from_entity
        entity: number
forms:
  restaurant_form:
    ignored_intents:
      - chitchat
    required_slots:
      - cuisine
      - num_people
```

Once the form action gets called for the first time, the form gets activated and will prompt the user for the next required slot value. It does this by looking for a [response](https://legacy-docs-oss.rasa.com/docs/rasa/responses) called `utter_ask_<form_name>_<slot_name>` or `utter_ask_<slot_name>` if the former isn't found. Make sure to define these responses in your domain file for each required slot.

### Activating a Form

To activate a form you need to add a [story](https://legacy-docs-oss.rasa.com/docs/rasa/stories) or [rule](https://legacy-docs-oss.rasa.com/docs/rasa/rules), which describes when the assistant should run the form. In the case a specific intent triggering a form, you can for example use the following rule:

```yaml
rules:
  - rule: Activate form
    steps:
      - intent: request_restaurant
      - action: restaurant_form
      - active_loop: restaurant_form
```

##### note

The `active_loop: restaurant_form` step indicates that the form should be activated after `restaurant_form` was run.

### Deactivating a Form

A form will automatically deactivate itself once all required slots are filled. You can describe your assistant's behavior for the end of a form with a rule or a story. If you don't add an applicable story or rule, the assistant will automatically listen for the next user message after the form is finished. The following example runs the utterances `utter_submit` and `utter_slots_values` as soon as the form `your_form` filled all required slots.

```yaml
rules:
  - rule: Submit form
    condition:
      - active_loop: restaurant_form
    steps:
      - action: restaurant_form
        active_loop: null
      - slot_was_set:
          - requested_slot: null
      - action: utter_submit
      - action: utter_slots_values
```

Users might want to break out of a form early. Please see [Writing Stories / Rules for Unhappy Form Paths](https://legacy-docs-oss.rasa.com/docs/rasa/forms#writing-stories--rules-for-unhappy-form-paths) on how to write stories or rules for this case.

### Slot Mappings

##### Changed in 3.0

As of 3.0, [slot mappings](https://legacy-docs-oss.rasa.com/docs/rasa/domain#slot-mappings) are defined in the `slots` section of the domain. This change allows the same slot mapping to be reused across multiple forms, removing any unnecessary duplication. Please follow the [migration guide](https://legacy-docs-oss.rasa.com/docs/rasa/migration-guide#slot-mappings) to update your assistant.

Note specifically the role of [Mapping Conditions](https://legacy-docs-oss.rasa.com/docs/rasa/domain#mapping-conditions) and the [unique entity mapping](https://legacy-docs-oss.rasa.com/docs/rasa/domain#unique-from_entity-mapping-matching) constraint.

### Writing Stories / Rules for Unhappy Form Paths

Your users will not always respond with the information you ask of them. Typically, users will ask questions, make chitchat, change their mind, or otherwise stray from the happy path.

While a form is active, if a user's input does not fill the requested slot, the execution of the form action will be rejected i.e. the form will automatically raise an `ActionExecutionRejection`. These are the specific scenarios in which a form will raise an `ActionExecutionRejection`:

- a slot was requested, but the user didn't fill the slot with their last message and you didn't define a custom action for [validating slots](https://legacy-docs-oss.rasa.com/docs/rasa/forms#validating-form-input) or [extracting slots](https://legacy-docs-oss.rasa.com/docs/rasa/forms#custom-slot-mappings).
- a slot was requested, but your custom action for [validating slots](https://legacy-docs-oss.rasa.com/docs/rasa/forms#validating-form-input) or [extracting slots](https://legacy-docs-oss.rasa.com/docs/rasa/forms#custom-slot-mappings) didn't return any `SlotSet` events.

To intentionally reject the form execution, you can also return an `ActionExecutionRejected` event as part of your custom validations or slot mappings.

To handle situations that might cause a form's execution to be rejected, you can write rules or stories that include the expected interruptions. For example, if you expect your users to chitchat with your bot, you could add a rule to handle this:

```yaml
rules:
  - rule: Example of an unhappy path
    condition:
      - active_loop: restaurant_form
    steps:
      - intent: chitchat
      - action: utter_chitchat
      - action: restaurant_form
        active_loop: restaurant_form
```

In some situations, users may change their mind in the middle of the form action and decide not to go forward with their initial request. In cases like this, the assistant should stop asking for the requested slots.

You can handle such situations gracefully using a default action `action_deactivate_loop` which will deactivate the form and reset the requested slot. An example story of such conversation could look as follows:

```yaml
stories:
  - story: User interrupts the form and doesn't want to continue
    steps:
      - intent: request_restaurant
      - action: restaurant_form
        active_loop: restaurant_form
      - intent: stop
      - action: utter_ask_continue
      - intent: stop
      - action: action_deactivate_loop
        active_loop: null
```

It is **strongly** recommended that you build these rules or stories using [interactive learning](https://legacy-docs-oss.rasa.com/docs/rasa/writing-stories#using-interactive-learning). If you write these rules / stories by hand you will likely miss important things.

## Advanced Usage

Forms are fully customizable using [Custom Actions](https://legacy-docs-oss.rasa.com/docs/rasa/actions#custom-actions).

### Validating Form Input

After extracting a slot value from user input, you can validate the extracted slots. By default Rasa only validates if any slot was filled after requesting a slot.

You can implement a [Custom Action](https://legacy-docs-oss.rasa.com/docs/rasa/actions#custom-actions)`validate_<form_name>` to validate any extracted slots. Make sure to add this action to the `actions` section of your domain:

```yaml
actions:
  - validate_restaurant_form
```

When the form is executed it will run your custom action after every user turn to validate the latest filled slots.

This custom action can extend `FormValidationAction` class to simplify the process of validating extracted slots. In this case, you need to write functions named `validate_<slot_name>` for every extracted slot.

The following example shows the implementation of a custom action which validates that the slot named `cuisine` is valid.

```python
from typing import Text, List, Any, Dict
from rasa_sdk import Tracker, FormValidationAction
from rasa_sdk.executor import CollectingDispatcher
from rasa_sdk.types import DomainDict

class ValidateRestaurantForm(FormValidationAction):
    def name(self) -> Text:
        return "validate_restaurant_form"

@staticmethod
    def cuisine_db() -> List[Text]:
        return ["caribbean", "chinese", "french"]

def validate_cuisine(
            self,
            slot_value: Any,
            dispatcher: CollectingDispatcher,
            tracker: Tracker,
            domain: DomainDict,
    ) -> Dict[Text, Any]:
        if slot_value.lower() in self.cuisine_db():
            return {"cuisine": slot_value}
        else:
            return {"cuisine": None}
```

You can also extend the `Action` class and retrieve extracted slots with `tracker.slots_to_validate` to fully customize the validation process.

### Custom Slot Mappings

##### Changed in 3.0

The `slots_mapped_in_domain` argument provided to the `required_slots` method of `FormValidationAction` has been replaced by the `domain_slots` argument, please update your custom actions to the new argument name.

If none of the predefined [Slot Mappings](https://legacy-docs-oss.rasa.com/docs/rasa/domain#slot-mappings) fit your use case, you can use the [Custom Action](https://legacy-docs-oss.rasa.com/docs/rasa/forms/#custom-slot-mappings)`validate_<form_name>` to write your own extraction code. Rasa will trigger this action when the form is run.

If you're using the Rasa SDK we recommend you to extend the provided `FormValidationAction`. When using the `FormValidationAction`, three steps are required to extract customs slots:

1. Define a method `extract_<slot_name>` for every slot that should be mapped in a custom way.
2. In your domain file, for your form's `required_slots`, list all required slots, with both predefined and custom mappings.
3. In addition, you can override the `required_slots` method to add dynamically requested slots.

##### note

If you have added a slot with a custom mapping in the `slots` section of the domain file which you only want to be validated within the context of a form by a custom action extending `FormValidationAction`, please make sure that this slot has a mapping of type `custom` and that the slot name is included in the form's `required_slots`.

The following example shows the implementation of a form which extracts a slot `outdoor_seating` in a custom way, in addition to the slots which use predefined mappings.

```python
from typing import Dict, Text, List, Optional, Any
from rasa_sdk import Tracker
from rasa_sdk.executor import CollectingDispatcher
from rasa_sdk.forms import FormValidationAction

class ValidateRestaurantForm(FormValidationAction):
    def name(self) -> Text:
        return "validate_restaurant_form"

async def extract_outdoor_seating(self,
            dispatcher: CollectingDispatcher,
            tracker: Tracker,
            domain: Dict
    ) -> Dict[Text, Any]:
        text_of_last_user_message = tracker.latest_message.get("text")
        sit_outside = "outdoor" in text_of_last_user_message
        return {"outdoor_seating": sit_outside}
```

By default the `FormValidationAction` will automatically set the [`requested_slot`](https://legacy-docs-oss.rasa.com/docs/rasa/forms#the-requested_slot-slot) to the first slot specified in `required_slots` which is not filled.

### Dynamic Form Behavior

By default, Rasa will ask for the next empty slot from the slots listed for your form in the domain file. If you use [custom slot mappings](https://legacy-docs-oss.rasa.com/docs/rasa/forms#custom-slot-mappings) and the `FormValidationAction`, it will ask for the first empty slot returned by the `required_slots` method. If all slots in `required_slots` are filled the form will be deactivated.

You can update the required slots of your form dynamically. This is, for example, useful when you need to fill additional slots based on how a previous slot was filled or when you want to change the order in which slots are requested.

If you are using the Rasa SDK, we strongly recommend that you use the `FormValidationAction` and override `required_slots` to fit your dynamic behavior. You must implement a method `extract_<slot name>` for every slot which doesn't use a predefined mapping, as described in [Custom Slot Mappings](https://legacy-docs-oss.rasa.com/docs/rasa/forms#custom-slot-mappings).

The example below will ask the user if they want to sit in the shade or in the sun in case they said they want to sit outside.

```python
from typing import Text, List, Optional
from rasa_sdk.forms import FormValidationAction

class ValidateRestaurantForm(FormValidationAction):
    def name(self) -> Text:
        return "validate_restaurant_form"

async def required_slots(
            self,
            domain_slots: List[Text],
            dispatcher: "CollectingDispatcher",
            tracker: "Tracker",
            domain: "DomainDict",
    ) -> List[Text]:
        additional_slots = ["outdoor_seating"]
        if tracker.slots.get("outdoor_seating") is True:
            additional_slots.append("shade_or_sun")
        return additional_slots + domain_slots
```

If conversely, you want to remove a slot from the form's `required_slots` defined in the domain file under certain conditions, you should copy the `domain_slots` over to a new variable and apply changes to that new variable instead of directly modifying `domain_slots`. Directly modifying `domain_slots` can cause unexpected behavior.

```python
from typing import Text, List, Optional
from rasa_sdk.forms import FormValidationAction

class ValidateBookingForm(FormValidationAction):
    def name(self) -> Text:
        return "validate_booking_form"

async def required_slots(
            self,
            domain_slots: List[Text],
            dispatcher: "CollectingDispatcher",
            tracker: "Tracker",
            domain: "DomainDict",
    ) -> List[Text]:
        updated_slots = domain_slots.copy()
        if tracker.slots.get("existing_customer") is True:
            updated_slots.remove("email_address")
        return updated_slots
```

### The requested_slot slot

The slot `requested_slot` is automatically added to the domain as a slot of type [`text`](https://legacy-docs-oss.rasa.com/docs/rasa/domain#text-slot). The value of the `requested_slot` will be ignored during conversations. If you want to change this behavior, you need to add the `requested_slot` to your domain file as a categorical slot with `influence_conversation` set to `true`. You might want to do this if you want to handle your unhappy paths differently, depending on what slot is currently being asked from the user. For example, if your users respond to one of the bot's questions with another question, like _why do you need to know that?_

The response to this `explain` intent depends on where we are in the story. In the restaurant case, your stories would look something like this:

```yaml
stories:
  - story: explain cuisine slot
    steps:
      - intent: request_restaurant
      - action: restaurant_form
        active_loop: restaurant
      - slot_was_set:
          - requested_slot: cuisine
      - intent: explain
      - action: utter_explain_cuisine
      - action: restaurant_form
        active_loop: null

- story: explain num_people slot
    steps:
      - intent: request_restaurant
      - action: restaurant_form
        active_loop: restaurant
      - slot_was_set:
          - requested_slot: cuisine
      - slot_was_set:
          - requested_slot: num_people
      - intent: explain
      - action: utter_explain_num_people
      - action: restaurant_form
        active_loop: null
```

Again, it is **strongly** recommended that you use [interactive learning](https://legacy-docs-oss.rasa.com/docs/rasa/writing-stories#using-interactive-learning) to build these stories.

### Using a Custom Action to Ask For the Next Slot

As soon as the form determines which slot has to be filled next by the user, it will execute the action `utter_ask_<form_name>_<slot_name>` or `utter_ask_<slot_name>` to ask the user to provide the necessary information. If a regular utterance is not enough, you can also use a custom action `action_ask_<form_name>_<slot_name>` or `action_ask_<slot_name>` to ask for the next slot.

```python
from typing import Dict, Text, List
from rasa_sdk import Tracker
from rasa_sdk.events import EventType
from rasa_sdk.executor import CollectingDispatcher
from rasa_sdk import Action

class AskForSlotAction(Action):
    def name(self) -> Text:
        return "action_ask_cuisine"

def run(
            self, dispatcher: CollectingDispatcher, tracker: Tracker, domain: Dict
    ) -> List[EventType]:
        dispatcher.utter_message(text="What cuisine?")
        return []
```

If there is more than one asking option for the slot, Rasa prioritizes in the following order:

1. `action_ask_<form_name>_<slot_name>`
2. `utter_ask_<form_name>_<slot_name>`
3. `action_ask_<slot_name>`
4. `utter_ask_<slot_name>`
