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 +4 -4
- data/.rubocop.yml +10 -0
- data/CHANGELOG.md +24 -0
- data/README.md +28 -0
- data/lib/aws/lex/conversation.rb +4 -0
- data/lib/aws/lex/conversation/base.rb +4 -1
- data/lib/aws/lex/conversation/exception/missing_handler.rb +12 -0
- data/lib/aws/lex/conversation/handler/base.rb +1 -1
- data/lib/aws/lex/conversation/handler/slot_resolution.rb +30 -0
- data/lib/aws/lex/conversation/type/base.rb +6 -2
- data/lib/aws/lex/conversation/type/event.rb +7 -1
- data/lib/aws/lex/conversation/type/{current_intent.rb → intent.rb} +4 -2
- data/lib/aws/lex/conversation/type/intent_confidence.rb +76 -0
- data/lib/aws/lex/conversation/version.rb +1 -1
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3d7e1994eb1948eceb9d236d65c0cc449e5120b6c91fb86585a7225e660cbc84
|
4
|
+
data.tar.gz: 071c8e344b1c6d78597986f679a7446250d2d2d75a001d4af7d393043c567529
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c83c69dfb06fd6faa16af4dfeb37119b84054c473e11929f201b34b419aba071918405635587ad38b21ce6d97862f60f1e360d9867059942deaea348a9eab176
|
7
|
+
data.tar.gz: f3cb8c0162884104b0ccb6f921c3cacdd08412d83b32e2463178b0a945b8e795de8da9bb99eabaaaf814de3b19b930e0a59972aea0dd128d8ca29d0da7e05982
|
data/.rubocop.yml
CHANGED
@@ -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
|
data/CHANGELOG.md
ADDED
@@ -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.
|
data/lib/aws/lex/conversation.rb
CHANGED
@@ -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/
|
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,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
|
-
|
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
|
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
|
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:
|
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-
|
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
|