aws-lex-conversation 6.0.0 → 6.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: