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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2c078b6b854b3df1824942655b7dd763f72dcc1a439c61e0830e1a644a0c2a65
4
- data.tar.gz: 8f279c911fceacdc67005e02a09b04bd854e71f30d1212a92ca9d6aff5fe1f75
3
+ metadata.gz: 33df2c318e3e0d2e3157ebe43e27b38855334518efa5fa957be0e1035e6454f2
4
+ data.tar.gz: 5f7db4ab55ef155967688aae6a85781b1fca26860574d45280cfceb2e410b730
5
5
  SHA512:
6
- metadata.gz: 41b0d384f766900de162b9b7a49d9e4ffc0375168c9f33a7c7e238dfe72fdeb7d21a9ad366c3f9f7b31b2dffde1b06c02a6f6c164fddec5453915bf4403cb8d7
7
- data.tar.gz: ab4d16c2892aa0f8bf2c25e67f133a64d6c7e583283a1a960f12602da69cedf83d7242960d789e77d03b43c3ba43411fbabfc8a147668cd4f79151eea078886e
6
+ metadata.gz: 4831b1eed996b95384635c446a2432efbb765900a8ef814c089ab4bbbfa5311708b0f71fe080d5b4910110448c698dfe855747bc2d9b9a8b19643743581b9c86
7
+ data.tar.gz: 53cdad241f6ace96a3ea1cb7be904f3518edbab40a403c8ab0641ea26cc6629ae1e0ce16b642cf08217a6f06017936e5865ac01be74e3fdeb620f4654edad52c
data/.rubocop.yml CHANGED
@@ -45,6 +45,8 @@ Style/Lambda:
45
45
  EnforcedStyle: literal
46
46
  Style/GuardClause:
47
47
  Enabled: false
48
+ Style/OpenStructUse:
49
+ Enabled: false
48
50
  Style/RedundantFetchBlock:
49
51
  Enabled: false
50
52
  Style/SlicingWithRange:
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, :maximum_elicitations, :conversation
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.maximum_elicitations = opts.fetch(:maximum_elicitations) { 0 }
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
- return false if maximum_elicitations.zero?
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
@@ -32,7 +32,7 @@ module Aws
32
32
  def value
33
33
  raise TypeError, 'use values for List-type slots' if shape.list?
34
34
 
35
- lex_value.interpreted_value
35
+ lex_value&.interpreted_value
36
36
  end
37
37
 
38
38
  # takes an array of slot values
@@ -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
@@ -3,7 +3,7 @@
3
3
  module Aws
4
4
  module Lex
5
5
  class Conversation
6
- VERSION = '6.0.0'
6
+ VERSION = '6.3.0'
7
7
  end
8
8
  end
9
9
  end
@@ -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.new(params)
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
- lex.session_state.active_contexts.delete_if { |c| c.name == name }
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.0.0
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-09-07 00:00:00.000000000 Z
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: