aws-lex-conversation 1.2.0 → 2.0.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: bb6d1313f30041f9896bccc30fe2511d7eb0f377f505dc3c3c8e153e2838151d
4
- data.tar.gz: aa0804ca414e0c68d982ae8ba601a7f46a729678130babb56d7eb87edf119cd3
3
+ metadata.gz: 3d7e1994eb1948eceb9d236d65c0cc449e5120b6c91fb86585a7225e660cbc84
4
+ data.tar.gz: 071c8e344b1c6d78597986f679a7446250d2d2d75a001d4af7d393043c567529
5
5
  SHA512:
6
- metadata.gz: 3471ce6eddb122e71cb81aad093e663165bae469b8317c045eddb4f3ba21412ed3815e3f9e895aea121a6af61ae143abb15c0cff1520c10243bfe51a807b09b8
7
- data.tar.gz: 906124b34ad1218de0c2725942620fc530120b855cf8e12552c8fc9b841fa18894a7327d92e908ff02e8e3bf276da7d1c4a951efcb87bb2c8f4dd19ecf057bc0
6
+ metadata.gz: c83c69dfb06fd6faa16af4dfeb37119b84054c473e11929f201b34b419aba071918405635587ad38b21ce6d97862f60f1e360d9867059942deaea348a9eab176
7
+ data.tar.gz: f3cb8c0162884104b0ccb6f921c3cacdd08412d83b32e2463178b0a945b8e795de8da9bb99eabaaaf814de3b19b930e0a59972aea0dd128d8ca29d0da7e05982
@@ -9,6 +9,14 @@ Documentation:
9
9
  Enabled: false
10
10
  Gemspec/RequiredRubyVersion:
11
11
  Enabled: false
12
+ Layout/FirstArrayElementIndentation:
13
+ Enabled: false
14
+ Layout/MultilineMethodCallIndentation:
15
+ Enabled: false
16
+ Layout/SpaceAroundOperators:
17
+ Enabled: false
18
+ Metrics/AbcSize:
19
+ Max: 18
12
20
  Metrics/ClassLength:
13
21
  Max: 150
14
22
  Metrics/BlockLength:
@@ -27,3 +35,5 @@ Style/Lambda:
27
35
  EnforcedStyle: literal
28
36
  Style/GuardClause:
29
37
  Enabled: false
38
+ Style/SlicingWithRange:
39
+ Enabled: false
@@ -0,0 +1,24 @@
1
+ # 2.0.0 - August 19, 2020
2
+
3
+ * **breaking change:** Rename `Aws::Lex::Conversation::Type::CurrentIntent` to `Aws::Lex::Conversation::Type::Intent`.
4
+ * **breaking change:** Built-in handlers now default the `options` attribute to an empty hash.
5
+ * Add Lex NLU model improvement functionality (see: https://aws.amazon.com/about-aws/whats-new/2020/08/amazon-lex-launches-accuracy-improvements-and-confidence-scores/).
6
+ * Add the `intent_confidence` method to the conversation class that may be used as follows:
7
+
8
+ ```ruby
9
+ # NOTE: Lex model improvements must be enabled on the bot to get confidence data.
10
+ # SEE: https://aws.amazon.com/about-aws/whats-new/2020/08/amazon-lex-launches-accuracy-improvements-and-confidence-scores/
11
+ conversation.intent_confidence.ambiguous? # true/false
12
+ conversation.intent_confidence.unambiguous? # true/false
13
+ conversation.intent_confidence.candidates # [...] the array contains the current_intent and all similar intents
14
+ conversation.intent_confidence.similar_alternates # [...] the array doesn't contain the current_intent
15
+ ```
16
+
17
+ * The calculation used to determine intent ambiguity by default looks for confidence scores that are within a standard deviation of the current intent's confidence score.
18
+ * You can pass your own static `threshold` parameter if you wish to change this behaviour:
19
+
20
+ ```ruby
21
+ conversation.intent_confidence.ambiguous?(threshold: 0.4) # true/false
22
+ ```
23
+
24
+ * Implement a built-in `SlotResolution` handler that is intended to act as the initial handler in the chain. This handler will resolve all slot values to their top resolution, then call the successor handler.
data/README.md CHANGED
@@ -141,6 +141,34 @@ conversation.handlers = [
141
141
  conversation.respond # => { dialogAction: { type: 'Delegate' } }
142
142
  ```
143
143
 
144
+ ### `Aws::Lex::Conversation::Handler::SlotResolution`
145
+
146
+ This handler will set all slot values equal to their top resolution in the input event. The handler then calls the next handler in the chain for a response.
147
+
148
+ **NOTE:** This handler must not be the final handler in the chain. An exception of type `Aws::Lex::Conversation::Exception::MissingHandler` will be raised if there is no successor handler.
149
+
150
+ | Option | Required | Description | Default Value |
151
+ |------------------|----------|--------------------------------------------------------------|-------------------------------------|
152
+ | respond_on | No | A callable that provides the condition for `will_handle?`. | `->(c) { true }` |
153
+
154
+ i.e.
155
+
156
+ ```ruby
157
+ conversation = Aws::Lex::Conversation.new(event: event, context: context)
158
+ conversation.handlers = [
159
+ {
160
+ handler: Aws::Lex::Conversation::Handler::SlotResolution
161
+ },
162
+ {
163
+ handler: Aws::Lex::Conversation::Handler::Delegate,
164
+ options: {
165
+ respond_on: ->(c) { true }
166
+ }
167
+ }
168
+ ]
169
+ conversation.respond # => { dialogAction: { type: 'Delegate' } }
170
+ ```
171
+
144
172
  ## Development
145
173
 
146
174
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -42,6 +42,10 @@ module Aws
42
42
  chain.first.handle(self)
43
43
  end
44
44
 
45
+ def intent_confidence
46
+ @intent_confidence ||= Type::IntentConfidence.new(event: lex)
47
+ end
48
+
45
49
  def intent_name
46
50
  lex.current_intent.name
47
51
  end
@@ -4,6 +4,7 @@ require 'json'
4
4
  require 'shrink/wrap'
5
5
 
6
6
  require_relative 'version'
7
+ require_relative 'exception/missing_handler'
7
8
  require_relative 'response/base'
8
9
  require_relative 'response/close'
9
10
  require_relative 'response/confirm_intent'
@@ -24,11 +25,12 @@ require_relative 'type/invocation_source'
24
25
  require_relative 'type/dialog_action_type'
25
26
  require_relative 'type/confirmation_status'
26
27
  require_relative 'type/fulfillment_state'
28
+ require_relative 'type/intent_confidence'
27
29
  require_relative 'type/recent_intent_summary_view'
28
30
  require_relative 'type/slot'
29
31
  require_relative 'type/slot_resolution'
30
32
  require_relative 'type/slot_detail'
31
- require_relative 'type/current_intent'
33
+ require_relative 'type/intent'
32
34
  require_relative 'type/output_dialog_mode'
33
35
  require_relative 'type/bot'
34
36
  require_relative 'type/message/content_type'
@@ -42,3 +44,4 @@ require_relative 'type/event'
42
44
  require_relative 'handler/base'
43
45
  require_relative 'handler/echo'
44
46
  require_relative 'handler/delegate'
47
+ require_relative 'handler/slot_resolution'
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aws
4
+ module Lex
5
+ class Conversation
6
+ module Exception
7
+ class MissingHandler < StandardError
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -9,7 +9,7 @@ module Aws
9
9
 
10
10
  def initialize(opts = {})
11
11
  self.successor = opts[:successor]
12
- self.options = opts[:options]
12
+ self.options = opts[:options] || {}
13
13
  end
14
14
 
15
15
  def will_respond?(conversation)
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aws
4
+ module Lex
5
+ class Conversation
6
+ module Handler
7
+ class SlotResolution < Base
8
+ def will_respond?(conversation)
9
+ # respond by default unless told otherwise
10
+ callable = options.fetch(:respond_on) { ->(_c) { true } }
11
+ callable.call(conversation)
12
+ end
13
+
14
+ def response(conversation)
15
+ # resolve all slots to their top resolution
16
+ conversation.slots.values.each(&:resolve!)
17
+
18
+ unless successor
19
+ msg = 'Handler `SlotResolution` must not be the final handler in the chain'
20
+ raise Exception::MissingHandler, msg
21
+ end
22
+
23
+ # call the next handler in the chain
24
+ successor.handle(conversation)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -53,8 +53,12 @@ module Aws
53
53
  end
54
54
 
55
55
  module ClassMethods
56
- def integer!
57
- ->(v) { v.to_i }
56
+ def integer!(nilable: false)
57
+ nilable ? ->(v) { v&.to_i } : ->(v) { v.to_i }
58
+ end
59
+
60
+ def float!(nilable: false)
61
+ nilable ? ->(v) { v&.to_f } : ->(v) { v.to_f }
58
62
  end
59
63
 
60
64
  def symbolize_hash!
@@ -7,6 +7,7 @@ module Aws
7
7
  class Event
8
8
  include Base
9
9
 
10
+ required :alternative_intents, default: -> { [] }
10
11
  required :current_intent
11
12
  required :bot
12
13
  required :user_id
@@ -20,8 +21,13 @@ module Aws
20
21
  optional :sentiment_response
21
22
  optional :kendra_response
22
23
 
24
+ computed_property :intents, ->(instance) do
25
+ [instance.current_intent] | instance.alternative_intents
26
+ end
27
+
23
28
  coerce(
24
- current_intent: CurrentIntent,
29
+ alternative_intents: Array[Intent],
30
+ current_intent: Intent,
25
31
  bot: Bot,
26
32
  invocation_source: InvocationSource,
27
33
  output_dialog_mode: OutputDialogMode,
@@ -4,13 +4,14 @@ module Aws
4
4
  module Lex
5
5
  class Conversation
6
6
  module Type
7
- class CurrentIntent
7
+ class Intent
8
8
  include Base
9
9
 
10
10
  required :name
11
11
  required :raw_slots, from: :slots, virtual: true
12
12
  required :slot_details
13
13
  required :confirmation_status
14
+ optional :nlu_intent_confidence_score
14
15
 
15
16
  computed_property :slots, ->(instance) do
16
17
  instance.raw_slots.each_with_object({}) do |(key, value), hash|
@@ -38,7 +39,8 @@ module Aws
38
39
 
39
40
  coerce(
40
41
  slot_details: slot_details!,
41
- confirmation_status: ConfirmationStatus
42
+ confirmation_status: ConfirmationStatus,
43
+ nlu_intent_confidence_score: float!(nilable: true)
42
44
  )
43
45
  end
44
46
  end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aws
4
+ module Lex
5
+ class Conversation
6
+ module Type
7
+ class IntentConfidence
8
+ include Base
9
+
10
+ required :event
11
+
12
+ def ambiguous?(threshold: standard_deviation)
13
+ candidates(threshold: threshold).size > 1
14
+ end
15
+
16
+ def unambiguous?(threshold: standard_deviation)
17
+ !ambiguous?(threshold: threshold)
18
+ end
19
+
20
+ # NOTE: by default this method looks for candidates
21
+ # with a confidence score within one standard deviation
22
+ # of the current intent. Confidence scores may not be
23
+ # normally distributed, so it's very possible that this
24
+ # method will return abnormal results for skewed sample sets.
25
+ #
26
+ # If you want a consistent threshold for the condition, pass
27
+ # a static `threshold` parameter.
28
+ def candidates(threshold: standard_deviation)
29
+ intents = event.intents.select do |intent|
30
+ diff = event.current_intent
31
+ .nlu_intent_confidence_score
32
+ .to_f
33
+ .-(intent.nlu_intent_confidence_score.to_f)
34
+ .abs
35
+
36
+ diff <= threshold
37
+ end
38
+
39
+ # sort descending
40
+ intents.sort do |a, b|
41
+ b.nlu_intent_confidence_score.to_f <=> a.nlu_intent_confidence_score.to_f
42
+ end
43
+ end
44
+
45
+ def mean
46
+ @mean ||= calculate_mean
47
+ end
48
+
49
+ def similar_alternates(threshold: standard_deviation)
50
+ # remove the first element (current intent) from consideration
51
+ candidates(threshold: threshold)[1..-1]
52
+ end
53
+
54
+ def standard_deviation
55
+ @standard_deviation ||= calculate_standard_deviation
56
+ end
57
+
58
+ private
59
+
60
+ def calculate_mean
61
+ sum = event.intents.sum { |i| i.nlu_intent_confidence_score.to_f }
62
+ sum / event.intents.size
63
+ end
64
+
65
+ def calculate_standard_deviation
66
+ normalized = event.intents.map do |intent|
67
+ (intent.nlu_intent_confidence_score.to_f - mean) ** 2
68
+ end
69
+ normalized_mean = normalized.sum / normalized.size
70
+ Math.sqrt(normalized_mean)
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -3,7 +3,7 @@
3
3
  module Aws
4
4
  module Lex
5
5
  class Conversation
6
- VERSION = '1.2.0'
6
+ VERSION = '2.0.0'
7
7
  end
8
8
  end
9
9
  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: 1.2.0
4
+ version: 2.0.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: 2020-07-22 00:00:00.000000000 Z
15
+ date: 2020-08-21 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: shrink_wrap
@@ -44,6 +44,7 @@ files:
44
44
  - ".rspec"
45
45
  - ".rubocop.yml"
46
46
  - ".simplecov"
47
+ - CHANGELOG.md
47
48
  - CODE_OF_CONDUCT.md
48
49
  - Gemfile
49
50
  - LICENSE.md
@@ -55,9 +56,11 @@ files:
55
56
  - lib/aws-lex-conversation.rb
56
57
  - lib/aws/lex/conversation.rb
57
58
  - lib/aws/lex/conversation/base.rb
59
+ - lib/aws/lex/conversation/exception/missing_handler.rb
58
60
  - lib/aws/lex/conversation/handler/base.rb
59
61
  - lib/aws/lex/conversation/handler/delegate.rb
60
62
  - lib/aws/lex/conversation/handler/echo.rb
63
+ - lib/aws/lex/conversation/handler/slot_resolution.rb
61
64
  - lib/aws/lex/conversation/response/base.rb
62
65
  - lib/aws/lex/conversation/response/close.rb
63
66
  - lib/aws/lex/conversation/response/confirm_intent.rb
@@ -72,11 +75,12 @@ files:
72
75
  - lib/aws/lex/conversation/type/base.rb
73
76
  - lib/aws/lex/conversation/type/bot.rb
74
77
  - lib/aws/lex/conversation/type/confirmation_status.rb
75
- - lib/aws/lex/conversation/type/current_intent.rb
76
78
  - lib/aws/lex/conversation/type/dialog_action_type.rb
77
79
  - lib/aws/lex/conversation/type/enumeration.rb
78
80
  - lib/aws/lex/conversation/type/event.rb
79
81
  - lib/aws/lex/conversation/type/fulfillment_state.rb
82
+ - lib/aws/lex/conversation/type/intent.rb
83
+ - lib/aws/lex/conversation/type/intent_confidence.rb
80
84
  - lib/aws/lex/conversation/type/invocation_source.rb
81
85
  - lib/aws/lex/conversation/type/message.rb
82
86
  - lib/aws/lex/conversation/type/message/content_type.rb