Source code for example_agent.shop

"""
This module demonstrates the use of intent **relations** to create complex
conversation flows.

.. note::

    Intent relations are under definition, they will be extended in next releases.

Let's break down a complex interaction involving :mod:`shop` intents.

#. 
   .. code-block:: text

       U: I want a fish
       A: What sort of fish do you want?

   Standard Intent :class:`OrderFish` starts the conversation.

   .. autoclass:: OrderFish

#. 
   .. code-block:: text

       U: A kipper
       A: Kipper, good choice. Adding 1 to cart

   Intent :class:`OrderFishAnswerKipper` follows :class:`OrderFish`. This means
   that the first can't be predicted if the latter wasn't predicted recently;
   this makes sense because an utterance like *"a kipper"* would sound really
   weird without context. Note that :class:`OrderFishAnswerKipper` is a subclass
   of :class:`OrderKipper`, and therefore inherits its
   :meth:`OrderKipper.fulfill` method.

   Check out the **source code** of the intents below to see how the *follow*
   relation is implemented.

   .. autoclass:: OrderKipper
      :members:

   .. autoclass:: OrderFishAnswerKipper
      :members:


#. 
   .. code-block:: text

       U: Actually I want more
       A: How many would you like?

   :class:`ChangeAmount` follows :class:`OrderKipper`. Since
   :class:`OrderFishAnswerKipper` is a subclass of :class:`OrderKipper`, our
   agent can predict :class:`ChangeAmount` at this point of the conversation.
   However, this intent defines a required parameter
   :attr:`ChangeAmount.amount`. Since *amount* can't be tagged in the user
   utterance, the Agent will respond with one of the slot filling prompts for
   parameter "amount" (see :mod:`intents.language`).

   .. autoclass:: ChangeAmount
      :members:

#. 
   .. code-block:: text

       U: 3 please
       A: Alright, I changed the amount to 3

   User fills the slot, and :class:`ChangeAmount` can finally be predicted.

.. autoclass:: CartApi
    :members:
"""
from dataclasses import dataclass

from intents import Intent, Sys, follow

#
# Helpers
#

[docs]class CartApi: """ A mock API for a Customer cart. In real life, this connects to a service of some sort. """
[docs] def add(self, item: str, amount: int): """ Add an item to cart Args: item: Name of the item to add amount: Amount to add """ print(f"If I was real, I'd add {amount} {item} to cart")
[docs] def update(self, item: str, amount: int): """ Update an item in cart Args: item: Name of the item to update amount: New amount to set """ print(f"If I was real, I'd update the amount of {item} to {amount}")
# # Intents #
[docs]@dataclass class OrderFish(Intent): """ | U: I'd like to buy a fish please | A: What sort of fish would you like? Args: amount: The amount of fish to buy """ lifespan = 3 amount: Sys.Integer = 1
[docs]@dataclass class OrderKipper(OrderFish): """ | U: I'd like to buy a kipper | A: Alright, adding 1 kipper to cart Args: amount: The amount of kipper to buy """
[docs] def fulfill(self, context, *args): """ Use :class:`CartApi` to add kippers to cart. The amount is specified by :attr:`OrderKipper.amount` """ cart = CartApi() cart.add('kipper', self.amount)
[docs]@dataclass class OrderFishAnswerKipper(OrderKipper): """ | ("...what sort of fish would you like?") | U: kipper | A: Kipper, good choice Args: amount: The amount of kipper to buy parent_order_fish: The OrderFish intent from context """ parent_order_fish: OrderFish = follow()
[docs]@dataclass class ChangeAmount(Intent): """ | ("...adding one kipper to cart") | U: actually, make it 2 | A: Sure, 2 kippers for you Args: amount: The new amount of kipper. Note that this overwrites all the other values for parameter "amount" in context, even if they come from other intents parent_order_kipper: The OrderKipper intent from context """ amount: Sys.Integer parent_order_kipper: OrderKipper = follow(new_lifespan=2)
[docs] def fulfill(self, context, *args): """ Use :class:`CartApi` to update the amount of kippers in cart to :attr:`ChangeAmount.amount` """ cart = CartApi() cart.update('kipper', self.amount)