Fulfillments

It is common for Intents to need some additional business logic to be fulfilled. For instance, an intent like “is it going to rain tomorrow?” should call a weather service of some sort in order to produce a response.

This is typically achieved through fulfillment calls. Those are functions that are called by the prediction service after an Intent is predicted, but before returning the prediction to client, typically through a REST webhook call. Their purpose is to run some logic and optionally change the default Intent response.

Intent classes may override their fulfill() method to specify the behavior for fulfillment calls:

from intents import Intent, FulfillmentContext

@dataclass
class UserAsksRain(Intent):
    """Is it going to rain tomorrow?"""
    when: Sys.Date

    def fulfill(self, context: FulfillmentContext, **kwargs) -> Intent:
        result = wheather_api.get_wheather(date=self.when)
        if result == 'rain':
            return AgentReportsRainyWheather()
        else:
            return AgentReportsGoodWeather()

Connectors are responsible for receiving fulfillment requests from Services, build the appropriate Intent instance, call fulfill() on it, and return its response in the correct Service format. A development server is included, to conveniently receive REST webhook calls and route them to a Connector; see run_dev_server() for details.

We notice that fulfill() returns Intent instances. These are used to trigger a new intent, that will produce the response. Return None if you do not wish to change the Intent default response.

Note

It is common for platforms to allow fulfillment calls to just produce a set of responses as a fulfillment result, without triggering a whole other intent. However, for now only triggers are supported, as they are the most general case. This may change in next releases.

Fulfillment flow

This is the typical flow of a fulfillment request:

  1. A prediction request is sent to Service, either from predict(), or through some native service integration (Dialogflow natively supports Telegram, Slack, and so on).

  2. Service predicts an Intent and its parameter values

  3. Service sends a fulfillment request to its configured endpoint (that’s us)

  4. Intents fulfillment framework receives the reuest and builds a FulfillmentRequest object.

  5. Fulfillment framework passes the FulfillmentRequest object to the fulfill() method of Connector.

  6. Connector parses the fulfillment request, and builds both the Intent object and a FulfillmentContext object

  7. Connector calls fulfill() on the intent object, passing the context

  8. fulfill() runs its business logic, and optionally return another intent to trigger

  9. Connector builds the fulfillment response, in a format that Service can understand

  10. Connector returns the response to the fulfillment framework

  11. Fulfillment framework returns the Connector’s response

Serving

The flow above requires you to serve an endpoint that Service can call. For development you can use the included development server (more details at run_dev_server()). This is the only function exposed by intents.fulfillment.

For production you must write your own; this is supposed to be fairly simple: only thing your endpoint should do is to build a FulfillmentRequest object out of request data, and call fulfill() on a Connector instance. The result will be a dictionary that you can return as it is.

run_dev_server(connector, host='', port=8000)[source]

Spawn a simple HTTP server to receive fulfillment requests from the outside. This is typically used in combination with some local tunneling solution such as ngrok

from intents.fulfillment import run_dev_server
from intents.connectors import DialogflowEsConnector, WebhookConfiguration
from example_agent import ExampleAgent

webhook = WebhookConfiguration('https://<MY-ADDRESS>.ngrok.io', {"X-Foo": "bar"})
df = DialogflowEsConnector(..., ExampleAgent, webhook_configuration=webhook)
df.upload()  # Will set webhook address in Dialogflow
run_dev_server(df)

After running the example above, prediction calls (either from df.predict() or from the Dialogflow UI) will result in Dialogflow calling the webhook endpoint for fulfillment. You can try it with the intents defined in example_agent.calculator.

Note that the server will only release its port after its Python process dies. This makes it inconvenient to run within a Python CLI, because it would be necessary to shut the whole interpreter down at each change. Auto reload is not supported yet, your best option is to make a script to instantiate Connector and run the server.

Warning

This server uses Python builtin http.server module, which as per documentation only implements basic security check. Also the implementation of request handling is very basic and not thoroughly tested. Therefore, it is recommended not to run this service in production

Parameters
  • connector (Connector) – A Connector to direct incoming requests to

  • host (str) – Optional custom host

  • port (str) – Optional custom port