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:
A prediction request is sent to Service, either from
predict()
, or through some native service integration (Dialogflow natively supports Telegram, Slack, and so on).Service predicts an Intent and its parameter values
Service sends a fulfillment request to its configured endpoint (that’s us)
Intents fulfillment framework receives the reuest and builds a
FulfillmentRequest
object.Fulfillment framework passes the
FulfillmentRequest
object to thefulfill()
method ofConnector
.Connector parses the fulfillment request, and builds both the Intent object and a
FulfillmentContext
objectConnector calls
fulfill()
on the intent object, passing the contextfulfill()
runs its business logic, and optionally return another intent to triggerConnector builds the fulfillment response, in a format that Service can understand
Connector returns the response to the fulfillment framework
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 inexample_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 tohost (
str
) – Optional custom hostport (
str
) – Optional custom port