"""
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)