aws-lex-conversation 1.2.0 → 2.0.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: 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