Globally Delaying Solution Responses
Introduction
In some cases it can feel more natural if the response from a solution is not immediate - as a real person would not be able to give complex answers instantly. This post is describing an approach to introducing this natural “thinking time” delay in a flexible and non-intrusive way, with a view to not having a heavy impact on solution development time.
Audience
As this document will discuss in detail an approach to solving this problem in real solutions it is intended for solution developers with at least a basic understanding of developing solutions.
Approach
The general process which we will look at here is:
- Define a global minimum response time (
minimumResponseTime
) - Before processing the input
- Store the time the user input is received (before it is processed)
- After all matching, processing and dialogue behaviour is complete
- Calculate the time taken so far to create the response
- Delay until this response time is at least
minimumResponseTime
- Give the (potentially delayed) response
- Discuss the potential to have different delays for specific actions
- One flow is a question which it would be natural for the solution to find “difficult” - then the response could be delayed more
- One flow is “hi” : “hello!” - so the response could easily be less delayed
Implementation
Global Variables
Global variables will be used to store the “input received” timestamp and the appropriate delays:
_messageReceivedTimestamp
This is the time the message was received and can be used to apply a specific delay to the response.
Value is a java Instant stored in Pre-processing via Instant.now()
Default: java.time.Instant.MIN
_minResponseTimeMillis
The minimum response time for this solution - all responses will be delayed until at least this amount of time has passed since the input was received.
The response time is defined in milliseconds, and is reset to _defaultMinResponseTimeMillis
for each transaction
Default: -1
_defaultMinResponseTimeMillis
This is the default minimum response time in milliseconds.
This value will be reapplied after every response in order to allow transaction-specific delays.
Default: 500
Pre Processing
In pre processing we set the minimum response time to the solution default - we do this at the start of every transaction so we can modify the per-transaction response time at any point in the matching without having to ensure we then set it back at some point.
import java.time.*
// On transaction start - set the minimum response time to the project default...
_minResponseTimeMillis = _defaultMinResponseTimeMillis
We then store “now” as the transaction start time stamp so that we can use it to work out how long the processing has taken in the post processing.
// ...and store "now" to work out how long to delay for on post-processing
_messageReceivedTimestamp = Instant.now()
Post Processing
In post processing we add the minimum response time to the start time - then we compare it to “now”. Then if “now” is before start + minimum response time we wait until start + minimum response time by applying a sleep(delay):
import java.time.*
import java.time.temporal.ChronoUnit
if(_messageReceivedTimestamp.isAfter(Instant.MIN)) {
// We have successfully set the message received timestamp - this should be part of pre-processing
def delayToApply = Instant.now().until(_messageReceivedTimestamp.plusMillis(_minResponseTimeMillis), ChronoUnit.MILLIS);
if(delayToApply > 0) {
println "Delaying response by " + delayToApply + " milliseconds";
sleep(delayToApply);
}
}
Using the Delay
Standard Delay
Once the implementation is in place there is no need to do anything in the solution if the standard delay is acceptable
Custom Delay
If in any particular scenario the solution developer wants to affect the minimum response time, simply setting the _minResponseTimeMillis
variable to a new value (in milliseconds) will result in the delay being calculated from this value during post processing.
If a particular question is intended to be particularly “difficult” to answer, so the solution would feel more natural if there is an increased delay, then adding a script node with the following script will result in a double delay (obviously the delay could be set to any millisecond value):
_minResponseTimeMillis = _defaultMinResponseTimeMillis * 2
If a particular question should be answered as soon as possible then simply clear the delay:
_minResponseTimeMillis = 0
You could (if the scenario made sense!) do this directly in a trigger condition:
custom >> delay >> %$NUMBER^{_minResponseTimeMillis = (long)(lob.numericValue as Double)}
Here you can see the number annotation is used to gather a user-specified delay and this is applied to the response.
Random Delay
It could be that the fixed response period starts to feel unnatural in it’s uniformity then it is possible to apply a randomising factor to ensure that there is a delay but that it is not constant.
Simply add a new variable to hold the randomising factor
_delayVariation
This is a factor by which the delay can vary.
-
0 = No variation.
The delay will be _minResponseTimeMillis -
1 = Max variation
the delay will be between + and - 50% of _minResponseTimeMillis
For example, if _minResponseTimeMillis is 500 and the _delayAccuracy is 0, then the actual response time will be 500 milliseconds. Changing the _delayAccuracy to 0.5 will result in actual response times between 375 and 625. Setting the _delayAccuracy to 1 will result in actual response times between 250 and 750.
Default: 0.5
Then in post-processing add the following before the delay is applied:
// If a randomisation is set, then apply this to the minimum response time before the calculation
if(_delayVariation > 0) {
int randomisedDelay = new java.util.Random().nextInt((int)(_minResponseTimeMillis * _delayVariation))
def centeringFactor = 1 - (_delayVariation * 0.5)
_minResponseTimeMillis = (long) (_minResponseTimeMillis * centeringFactor) + randomisedDelay
}
Complexity Delay
Another approach could be to define a typing speed and delay the answer by a factor of the number of words! This will be left as an exercise for the reader!