aws-lex-conversation 6.0.0 → 6.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +2 -0
- data/CHANGELOG.md +34 -0
- data/README.md +108 -0
- data/aws-lex-conversation.gemspec +1 -0
- data/lib/aws/lex/conversation/base.rb +1 -0
- data/lib/aws/lex/conversation/response/elicit_slot.rb +3 -2
- data/lib/aws/lex/conversation/slot/elicitation.rb +3 -5
- data/lib/aws/lex/conversation/type/checkpoint.rb +22 -0
- data/lib/aws/lex/conversation/type/dialog_action.rb +3 -1
- data/lib/aws/lex/conversation/type/slot.rb +1 -1
- data/lib/aws/lex/conversation/type/slot_elicitation_style.rb +17 -0
- data/lib/aws/lex/conversation/version.rb +1 -1
- data/lib/aws/lex/conversation.rb +27 -5
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 33df2c318e3e0d2e3157ebe43e27b38855334518efa5fa957be0e1035e6454f2
|
4
|
+
data.tar.gz: 5f7db4ab55ef155967688aae6a85781b1fca26860574d45280cfceb2e410b730
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4831b1eed996b95384635c446a2432efbb765900a8ef814c089ab4bbbfa5311708b0f71fe080d5b4910110448c698dfe855747bc2d9b9a8b19643743581b9c86
|
7
|
+
data.tar.gz: 53cdad241f6ace96a3ea1cb7be904f3518edbab40a403c8ab0641ea26cc6629ae1e0ce16b642cf08217a6f06017936e5865ac01be74e3fdeb620f4654edad52c
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,36 @@
|
|
1
|
+
# 6.3.0 - Nov 22, 2021
|
2
|
+
|
3
|
+
* Add support for the recently added `slotElicitationStyle` property when generating an `ElicitSlot` repsonse ([documentation](https://docs.aws.amazon.com/lexv2/latest/dg/using-spelling.html)).
|
4
|
+
|
5
|
+
You can generate an `ElicitSlot` response now with an optional `slot_elicitation_style` property to allow for spelling support:
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
conversation.elicit_slot(
|
9
|
+
slot_to_elicit: 'LastName',
|
10
|
+
slot_elicitation_style: 'SpellByWord' # one of Default, SpellByLetter, or SpellByWord
|
11
|
+
)
|
12
|
+
```
|
13
|
+
|
14
|
+
# 6.2.0 - Sept 28, 2021
|
15
|
+
|
16
|
+
* Add a `Aws::Lex::Conversation#restore_from!` method that accepts a checkpoint parameter. This method modifies the underlying conversation state to match the data from the saved checkpoint.
|
17
|
+
* Make the `dialog_action_type` parameter on `Aws::Lex::Conversation#checkpoint!` default to `Delegate` if not specified as a developer convenience.
|
18
|
+
* Allow developers to pass an optional `intent` override parameter on `Aws::Lex::Conversation#checkpoint!` for convenience.
|
19
|
+
* Update the README with advanced examples for the conversation stash and checkpoints.
|
20
|
+
|
21
|
+
# 6.1.1 - Sept 22, 2021
|
22
|
+
|
23
|
+
* renamed `maximum_elicitations` to `max_retries` and made it backwards compatible to make the param name clear, by default this value is zero, allowing each slot to elicit only once
|
24
|
+
|
25
|
+
# 6.1.0 - Sept 7, 2021
|
26
|
+
|
27
|
+
Added helper methods for clearing active contexts
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
conversation.clear_context!(name: 'test') # clears this specific active context
|
31
|
+
conversation.clear_all_contexts! # clears all current active contexts
|
32
|
+
```
|
33
|
+
|
1
34
|
# 6.0.0 - Sept 7, 2021
|
2
35
|
|
3
36
|
* **breaking change** - Modify `Aws::Lex::Conversation::Type::Base#computed_property` to accept a block instead of a callable argument. This is an internal class and should not require any application-level changes.
|
@@ -55,6 +88,7 @@ it 'creates an event' do
|
|
55
88
|
expect(event).to include_session_values(username: 'jane.doe')
|
56
89
|
end
|
57
90
|
```
|
91
|
+
|
58
92
|
* Add a few convenience methods to `Aws::Lex::Conversation` instances for dealing with active contexts:
|
59
93
|
- `#active_context(name:)`:
|
60
94
|
|
data/README.md
CHANGED
@@ -191,6 +191,114 @@ conversation.handlers = [
|
|
191
191
|
conversation.respond # => { dialogAction: { type: 'Delegate' } }
|
192
192
|
```
|
193
193
|
|
194
|
+
## Advanced Concepts
|
195
|
+
|
196
|
+
This library provides a few constructs to help manage complex interactions:
|
197
|
+
|
198
|
+
### Data Stash
|
199
|
+
|
200
|
+
`Aws::Lex::Conversation` instances implement a `stash` method that can be used to store temporary data within a single invocation.
|
201
|
+
|
202
|
+
A conversation's stashed data will not be persisted between multiple invocations of your lambda function.
|
203
|
+
|
204
|
+
The conversation stash is a great spot to store deserialized data from the session, or invocation-specific state that needs to be shared between handler classes.
|
205
|
+
|
206
|
+
This example illustrates how the stash can be used to store deserialized data from the session:
|
207
|
+
|
208
|
+
```ruby
|
209
|
+
# given we have JSON-serialized data in as a persisted session value
|
210
|
+
conversation.session[:user_data] = '{"name":"Jane","id":1234,"email":"test@example.com"}'
|
211
|
+
# we can deserialize the data into a Hash that we store in the conversation stash
|
212
|
+
conversation.stash[:user] = JSON.parse(conversation.session[:user_data])
|
213
|
+
# later on we can reference our stashed data (within the same invocation)
|
214
|
+
conversation.stash[:user] # => {"name"=>"Jane", "id"=>1234, "email"=>"test@example.com"}
|
215
|
+
```
|
216
|
+
|
217
|
+
### Checkpoints
|
218
|
+
|
219
|
+
A conversation may transition between many different topics as the interaction progresses. This type of state transition can be easily handled with checkpoints.
|
220
|
+
|
221
|
+
When a checkpoint is created, all intent and slot data is encoded and stored into a `checkpoints` session value. This data persists between invocations, and is not removed until the checkpoint is restored.
|
222
|
+
|
223
|
+
You can create a checkpoint as follows:
|
224
|
+
|
225
|
+
```ruby
|
226
|
+
# we're ready to fulfill the OrderFlowers intent, but we want to elicit another intent first
|
227
|
+
conversation.checkpoint!(
|
228
|
+
label: 'order_flowers',
|
229
|
+
dialog_action_type: 'Close' # defaults to 'Delegate' if not specified
|
230
|
+
)
|
231
|
+
conversation.elicit_intent(
|
232
|
+
messages: [
|
233
|
+
{
|
234
|
+
content: 'Thanks! Before I place your order, is there anything else I can help with?',
|
235
|
+
contentType: 'PlainText'
|
236
|
+
}
|
237
|
+
]
|
238
|
+
)
|
239
|
+
```
|
240
|
+
|
241
|
+
You can restore the checkpoint in one of two ways:
|
242
|
+
|
243
|
+
```ruby
|
244
|
+
# in a future invocation, we can fetch an instance of the checkpoint and easily
|
245
|
+
# restore the conversation to the previous state
|
246
|
+
checkpoint = conversation.checkpoint(label: 'order_flowers')
|
247
|
+
checkpoint.restore!(
|
248
|
+
fulfillment_state: 'Fulfilled',
|
249
|
+
messages: [
|
250
|
+
{
|
251
|
+
content: 'Okay, your flowers have been ordered! Thanks!',
|
252
|
+
contentType: 'PlainText'
|
253
|
+
}
|
254
|
+
]
|
255
|
+
) # => our response object to Lex is returned
|
256
|
+
```
|
257
|
+
|
258
|
+
It's also possible to restore state from a checkpoint and utilize the conversation's handler chain:
|
259
|
+
|
260
|
+
```ruby
|
261
|
+
class AnotherIntent < Aws::Lex::Conversation::Handler::Base
|
262
|
+
def will_respond?(conversation)
|
263
|
+
conversation.intent_name == 'AnotherIntent' &&
|
264
|
+
conversation.checkpoint?(label: 'order_flowers')
|
265
|
+
end
|
266
|
+
|
267
|
+
def response(conversation)
|
268
|
+
checkpoint = conversation.checkpoint(label: 'order_flowers')
|
269
|
+
# replace the conversation's current resolved intent/slot data with the saved checkpoint data
|
270
|
+
conversation.restore_from!(checkpoint)
|
271
|
+
# call the next handler in the chain to produce a response
|
272
|
+
successor.handle(conversation)
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
class OrderFlowers < Aws::Lex::Conversation::Handler::Base
|
277
|
+
def will_respond?(conversation)
|
278
|
+
conversation.intent_name == 'OrderFlowers'
|
279
|
+
end
|
280
|
+
|
281
|
+
def response(conversation)
|
282
|
+
conversation.close(
|
283
|
+
fulfillment_state: 'Fulfilled',
|
284
|
+
messages: [
|
285
|
+
{
|
286
|
+
content: 'Okay, your flowers have been ordered! Thanks!',
|
287
|
+
contentType: 'PlainText'
|
288
|
+
}
|
289
|
+
]
|
290
|
+
)
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
conversation = Aws::Lex::Conversation.new(event: event, context: context)
|
295
|
+
conversation.handlers = [
|
296
|
+
{ handler: AnotherIntent },
|
297
|
+
{ handler: OrderFlowers }
|
298
|
+
]
|
299
|
+
conversation.respond # => returns a Lex response object
|
300
|
+
```
|
301
|
+
|
194
302
|
## Test Helpers
|
195
303
|
|
196
304
|
This library provides convenience methods to make testing easy! You can use the test helpers as follows:
|
@@ -26,6 +26,7 @@ Gem::Specification.new do |spec|
|
|
26
26
|
spec.license = 'MIT'
|
27
27
|
spec.required_ruby_version = Gem::Requirement.new('>= 2.3.0')
|
28
28
|
spec.metadata['homepage_uri'] = spec.homepage
|
29
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
29
30
|
|
30
31
|
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
31
32
|
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
@@ -25,6 +25,7 @@ require_relative 'type/sentiment_score'
|
|
25
25
|
require_relative 'type/sentiment_response'
|
26
26
|
require_relative 'type/invocation_source'
|
27
27
|
require_relative 'type/dialog_action_type'
|
28
|
+
require_relative 'type/slot_elicitation_style'
|
28
29
|
require_relative 'type/dialog_action'
|
29
30
|
require_relative 'type/confirmation_state'
|
30
31
|
require_relative 'type/fulfillment_state'
|
@@ -5,7 +5,7 @@ module Aws
|
|
5
5
|
class Conversation
|
6
6
|
module Response
|
7
7
|
class ElicitSlot < Base
|
8
|
-
attr_accessor :slot_to_elicit
|
8
|
+
attr_accessor :slot_to_elicit, :slot_elicitation_style
|
9
9
|
|
10
10
|
def initialize(opts = {})
|
11
11
|
super
|
@@ -16,7 +16,8 @@ module Aws
|
|
16
16
|
def dialog_action
|
17
17
|
Aws::Lex::Conversation::Type::DialogAction.shrink_wrap(
|
18
18
|
type: 'ElicitSlot',
|
19
|
-
slotToElicit: slot_to_elicit
|
19
|
+
slotToElicit: slot_to_elicit,
|
20
|
+
slotElicitationStyle: slot_elicitation_style
|
20
21
|
)
|
21
22
|
end
|
22
23
|
end
|
@@ -6,7 +6,7 @@ module Aws
|
|
6
6
|
module Slot
|
7
7
|
class Elicitation
|
8
8
|
attr_accessor :name, :elicit, :messages, :follow_up_messages,
|
9
|
-
:fallback, :
|
9
|
+
:fallback, :max_retries, :conversation
|
10
10
|
|
11
11
|
def initialize(opts = {})
|
12
12
|
self.name = opts.fetch(:name)
|
@@ -14,7 +14,7 @@ module Aws
|
|
14
14
|
self.messages = opts.fetch(:messages)
|
15
15
|
self.follow_up_messages = opts.fetch(:follow_up_messages) { opts.fetch(:messages) }
|
16
16
|
self.fallback = opts[:fallback]
|
17
|
-
self.
|
17
|
+
self.max_retries = opts[:max_retries] || opts[:maximum_elicitations] || 0
|
18
18
|
end
|
19
19
|
|
20
20
|
def elicit!
|
@@ -53,9 +53,7 @@ module Aws
|
|
53
53
|
end
|
54
54
|
|
55
55
|
def maximum_elicitations?
|
56
|
-
|
57
|
-
|
58
|
-
elicitation_attempts > maximum_elicitations
|
56
|
+
elicitation_attempts > max_retries
|
59
57
|
end
|
60
58
|
|
61
59
|
def first_elicitation?
|
@@ -20,6 +20,28 @@ module Aws
|
|
20
20
|
fulfillment_state: FulfillmentState
|
21
21
|
)
|
22
22
|
|
23
|
+
class << self
|
24
|
+
def build(opts = {})
|
25
|
+
new(normalize_parameters(opts))
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def normalize_parameters(opts)
|
31
|
+
params = opts.dup # we don't want to mutate our arguments
|
32
|
+
|
33
|
+
if params[:dialog_action_type].is_a?(String)
|
34
|
+
params[:dialog_action_type] = DialogActionType.new(params[:dialog_action_type])
|
35
|
+
end
|
36
|
+
|
37
|
+
if params[:fulfillment_state].is_a?(String)
|
38
|
+
params[:fulfillment_state] = FulfillmentState.new(params[:fulfillment_state])
|
39
|
+
end
|
40
|
+
|
41
|
+
params
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
23
45
|
# restore the checkpoint AND remove it from session
|
24
46
|
def restore!(conversation, opts = {})
|
25
47
|
conversation.checkpoints.delete_if { |c| c.label == label }
|
@@ -8,10 +8,12 @@ module Aws
|
|
8
8
|
include Base
|
9
9
|
|
10
10
|
optional :slot_to_elicit
|
11
|
+
optional :slot_elicitation_style, default: -> { 'Default' }
|
11
12
|
required :type
|
12
13
|
|
13
14
|
coerce(
|
14
|
-
type: DialogActionType
|
15
|
+
type: DialogActionType,
|
16
|
+
slot_elicitation_style: SlotElicitationStyle
|
15
17
|
)
|
16
18
|
end
|
17
19
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Aws
|
4
|
+
module Lex
|
5
|
+
class Conversation
|
6
|
+
module Type
|
7
|
+
class SlotElicitationStyle
|
8
|
+
include Enumeration
|
9
|
+
|
10
|
+
enumeration('Default')
|
11
|
+
enumeration('SpellByLetter')
|
12
|
+
enumeration('SpellByWord')
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/aws/lex/conversation.rb
CHANGED
@@ -62,9 +62,9 @@ module Aws
|
|
62
62
|
label = opts.fetch(:label)
|
63
63
|
params = {
|
64
64
|
label: label,
|
65
|
-
dialog_action_type: opts.fetch(:dialog_action_type),
|
65
|
+
dialog_action_type: opts.fetch(:dialog_action_type) { 'Delegate' },
|
66
66
|
fulfillment_state: opts[:fulfillment_state],
|
67
|
-
intent: lex.current_intent,
|
67
|
+
intent: opts.fetch(:intent) { lex.current_intent },
|
68
68
|
slot_to_elicit: opts[:slot_to_elicit]
|
69
69
|
}.compact
|
70
70
|
|
@@ -72,9 +72,8 @@ module Aws
|
|
72
72
|
# update the existing checkpoint
|
73
73
|
checkpoint(label: label).assign_attributes!(params)
|
74
74
|
else
|
75
|
-
# push a new checkpoint to the recent_intent_summary_view
|
76
75
|
checkpoints.unshift(
|
77
|
-
Type::Checkpoint.
|
76
|
+
Type::Checkpoint.build(params)
|
78
77
|
)
|
79
78
|
end
|
80
79
|
end
|
@@ -91,6 +90,21 @@ module Aws
|
|
91
90
|
lex.session_state.session_attributes.checkpoints
|
92
91
|
end
|
93
92
|
|
93
|
+
def restore_from!(checkpoint)
|
94
|
+
# we're done with the stored checkpoint once it's been restored
|
95
|
+
checkpoints.delete_if { |c| c.label == checkpoint.label }
|
96
|
+
# remove any memoized intent data
|
97
|
+
lex.current_intent = nil
|
98
|
+
# replace the intent with data from the checkpoint
|
99
|
+
lex.session_state.intent = checkpoint.intent
|
100
|
+
dialog_action = Type::DialogAction.new(
|
101
|
+
type: checkpoint.dialog_action_type,
|
102
|
+
slot_to_elicit: checkpoint.slot_to_elicit
|
103
|
+
)
|
104
|
+
lex.session_state.dialog_action = dialog_action
|
105
|
+
self
|
106
|
+
end
|
107
|
+
|
94
108
|
def active_context?(name:)
|
95
109
|
!active_context(name: name).nil?
|
96
110
|
end
|
@@ -104,7 +118,7 @@ module Aws
|
|
104
118
|
instance = active_context(name: name)
|
105
119
|
|
106
120
|
if instance
|
107
|
-
|
121
|
+
clear_context!(name: name)
|
108
122
|
else
|
109
123
|
instance = Type::Context.new
|
110
124
|
end
|
@@ -120,6 +134,14 @@ module Aws
|
|
120
134
|
instance
|
121
135
|
end
|
122
136
|
|
137
|
+
def clear_context!(name:)
|
138
|
+
lex.session_state.active_contexts.delete_if { |c| c.name == name }
|
139
|
+
end
|
140
|
+
|
141
|
+
def clear_all_contexts!
|
142
|
+
lex.session_state.active_contexts = []
|
143
|
+
end
|
144
|
+
|
123
145
|
def stash
|
124
146
|
@stash ||= {}
|
125
147
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: aws-lex-conversation
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 6.
|
4
|
+
version: 6.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jesse Doyle
|
@@ -12,7 +12,7 @@ authors:
|
|
12
12
|
autorequire:
|
13
13
|
bindir: exe
|
14
14
|
cert_chain: []
|
15
|
-
date: 2021-
|
15
|
+
date: 2021-11-22 00:00:00.000000000 Z
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
18
18
|
name: shrink_wrap
|
@@ -101,6 +101,7 @@ files:
|
|
101
101
|
- lib/aws/lex/conversation/type/session_attributes.rb
|
102
102
|
- lib/aws/lex/conversation/type/session_state.rb
|
103
103
|
- lib/aws/lex/conversation/type/slot.rb
|
104
|
+
- lib/aws/lex/conversation/type/slot_elicitation_style.rb
|
104
105
|
- lib/aws/lex/conversation/type/slot_shape.rb
|
105
106
|
- lib/aws/lex/conversation/type/slot_value.rb
|
106
107
|
- lib/aws/lex/conversation/type/time_to_live.rb
|
@@ -110,6 +111,7 @@ licenses:
|
|
110
111
|
- MIT
|
111
112
|
metadata:
|
112
113
|
homepage_uri: https://github.com/amaabca/aws-lex-conversation
|
114
|
+
rubygems_mfa_required: 'true'
|
113
115
|
post_install_message:
|
114
116
|
rdoc_options: []
|
115
117
|
require_paths:
|