aws-lex-conversation 0.5.0 → 1.2.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/README.md +3 -3
- data/lib/aws/lex/conversation.rb +1 -1
- data/lib/aws/lex/conversation/base.rb +5 -1
- data/lib/aws/lex/conversation/slot/elicitation.rb +80 -0
- data/lib/aws/lex/conversation/slot/elicitor.rb +35 -0
- data/lib/aws/lex/conversation/support/mixins/responses.rb +58 -0
- data/lib/aws/lex/conversation/support/mixins/slot_elicitation.rb +48 -0
- data/lib/aws/lex/conversation/type/base.rb +23 -2
- data/lib/aws/lex/conversation/type/current_intent.rb +19 -6
- data/lib/aws/lex/conversation/type/slot.rb +51 -0
- data/lib/aws/lex/conversation/version.rb +1 -1
- metadata +7 -3
- data/lib/aws/lex/conversation/support/responses.rb +0 -56
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bb6d1313f30041f9896bccc30fe2511d7eb0f377f505dc3c3c8e153e2838151d
|
4
|
+
data.tar.gz: aa0804ca414e0c68d982ae8ba601a7f46a729678130babb56d7eb87edf119cd3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3471ce6eddb122e71cb81aad093e663165bae469b8317c045eddb4f3ba21412ed3815e3f9e895aea121a6af61ae143abb15c0cff1520c10243bfe51a807b09b8
|
7
|
+
data.tar.gz: 906124b34ad1218de0c2725942620fc530120b855cf8e12552c8fc9b841fa18894a7327d92e908ff02e8e3bf276da7d1c4a951efcb87bb2c8f4dd19ecf057bc0
|
data/README.md
CHANGED
@@ -58,13 +58,13 @@ The first handler that returns `true` for the `will_respond?` method will provid
|
|
58
58
|
```ruby
|
59
59
|
class SayHello < Aws::Lex::Conversation::Handler::Base
|
60
60
|
def will_respond?(conversation)
|
61
|
-
conversation.lex.
|
61
|
+
conversation.lex.invocation_source.dialog_code_hook? && # callback is for DialogCodeHook (i.e. validation)
|
62
62
|
conversation.lex.current_intent.name == 'SayHello' && # Lex has routed to the 'SayHello' intent
|
63
|
-
conversation.slots[:name]
|
63
|
+
conversation.slots[:name].filled? # our expected slot value is set
|
64
64
|
end
|
65
65
|
|
66
66
|
def response(conversation)
|
67
|
-
name = conversation.slots[:name]
|
67
|
+
name = conversation.slots[:name].value
|
68
68
|
|
69
69
|
# NOTE: you can use the Type::* classes if you wish. The final output
|
70
70
|
# will be normalized to a value that complies with the Lex response format.
|
data/lib/aws/lex/conversation.rb
CHANGED
@@ -10,8 +10,11 @@ require_relative 'response/confirm_intent'
|
|
10
10
|
require_relative 'response/delegate'
|
11
11
|
require_relative 'response/elicit_intent'
|
12
12
|
require_relative 'response/elicit_slot'
|
13
|
-
require_relative 'support/responses'
|
13
|
+
require_relative 'support/mixins/responses'
|
14
|
+
require_relative 'support/mixins/slot_elicitation'
|
14
15
|
require_relative 'support/inflector'
|
16
|
+
require_relative 'slot/elicitation'
|
17
|
+
require_relative 'slot/elicitor'
|
15
18
|
require_relative 'type/base'
|
16
19
|
require_relative 'type/enumeration'
|
17
20
|
require_relative 'type/sentiment_label'
|
@@ -22,6 +25,7 @@ require_relative 'type/dialog_action_type'
|
|
22
25
|
require_relative 'type/confirmation_status'
|
23
26
|
require_relative 'type/fulfillment_state'
|
24
27
|
require_relative 'type/recent_intent_summary_view'
|
28
|
+
require_relative 'type/slot'
|
25
29
|
require_relative 'type/slot_resolution'
|
26
30
|
require_relative 'type/slot_detail'
|
27
31
|
require_relative 'type/current_intent'
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Aws
|
4
|
+
module Lex
|
5
|
+
class Conversation
|
6
|
+
module Slot
|
7
|
+
class Elicitation
|
8
|
+
attr_accessor :name, :elicit, :message, :follow_up_message,
|
9
|
+
:content_type, :fallback, :maximum_elicitations,
|
10
|
+
:conversation
|
11
|
+
|
12
|
+
def initialize(opts = {})
|
13
|
+
self.name = opts.fetch(:name)
|
14
|
+
self.elicit = opts.fetch(:elicit) { ->(_c) { true } }
|
15
|
+
self.message = opts.fetch(:message)
|
16
|
+
self.follow_up_message = opts.fetch(:follow_up_message) { opts.fetch(:message) }
|
17
|
+
self.content_type = opts.fetch(:content_type) do
|
18
|
+
Aws::Lex::Conversation::Type::Message::ContentType.new('PlainText')
|
19
|
+
end
|
20
|
+
self.fallback = opts.fetch(:fallback) { ->(_c) {} }
|
21
|
+
self.maximum_elicitations = opts.fetch(:maximum_elicitations) { 0 }
|
22
|
+
end
|
23
|
+
|
24
|
+
def elicit!
|
25
|
+
return fallback.call(conversation) if maximum_elicitations?
|
26
|
+
|
27
|
+
increment_slot_elicitations!
|
28
|
+
conversation.elicit_slot(
|
29
|
+
slot_to_elicit: name,
|
30
|
+
message: {
|
31
|
+
contentType: content_type,
|
32
|
+
content: elicitation_content
|
33
|
+
}
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
def elicit?
|
38
|
+
elicit.call(conversation) && !slot.filled?
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def slot
|
44
|
+
conversation.slots[name.to_sym]
|
45
|
+
end
|
46
|
+
|
47
|
+
def elicitation_content
|
48
|
+
first_elicitation? ? compose_message(message) : compose_message(follow_up_message)
|
49
|
+
end
|
50
|
+
|
51
|
+
def compose_message(msg)
|
52
|
+
msg.is_a?(Proc) ? msg.call(conversation) : msg
|
53
|
+
end
|
54
|
+
|
55
|
+
def increment_slot_elicitations!
|
56
|
+
conversation.session[session_key] = elicitation_attempts + 1
|
57
|
+
end
|
58
|
+
|
59
|
+
def maximum_elicitations?
|
60
|
+
return false if maximum_elicitations.zero?
|
61
|
+
|
62
|
+
elicitation_attempts > maximum_elicitations
|
63
|
+
end
|
64
|
+
|
65
|
+
def first_elicitation?
|
66
|
+
elicitation_attempts == 1
|
67
|
+
end
|
68
|
+
|
69
|
+
def session_key
|
70
|
+
:"SlotElicitations_#{name}"
|
71
|
+
end
|
72
|
+
|
73
|
+
def elicitation_attempts
|
74
|
+
conversation.session[session_key].to_i
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Aws
|
4
|
+
module Lex
|
5
|
+
class Conversation
|
6
|
+
module Slot
|
7
|
+
class Elicitor
|
8
|
+
attr_accessor :conversation, :elicitations
|
9
|
+
|
10
|
+
def initialize(opts = {})
|
11
|
+
self.conversation = opts.fetch(:conversation)
|
12
|
+
self.elicitations = opts.fetch(:elicitations) { [] }
|
13
|
+
elicitations.each do |elicitation|
|
14
|
+
elicitation.conversation = conversation
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def elicit?
|
19
|
+
incomplete_elicitations.any?
|
20
|
+
end
|
21
|
+
|
22
|
+
def elicit!
|
23
|
+
incomplete_elicitations.first.elicit! if elicit?
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def incomplete_elicitations
|
29
|
+
elicitations.select(&:elicit?)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Aws
|
4
|
+
module Lex
|
5
|
+
class Conversation
|
6
|
+
module Support
|
7
|
+
module Mixins
|
8
|
+
module Responses
|
9
|
+
def close(opts = {})
|
10
|
+
params = {
|
11
|
+
session_attributes: lex.session_attributes,
|
12
|
+
recent_intent_summary_view: lex.recent_intent_summary_view
|
13
|
+
}.merge(opts)
|
14
|
+
Response::Close.new(params).to_lex
|
15
|
+
end
|
16
|
+
|
17
|
+
def confirm_intent(opts = {})
|
18
|
+
params = {
|
19
|
+
session_attributes: lex.session_attributes,
|
20
|
+
recent_intent_summary_view: lex.recent_intent_summary_view,
|
21
|
+
intent_name: lex.current_intent.name,
|
22
|
+
slots: lex.current_intent.slots
|
23
|
+
}.merge(opts)
|
24
|
+
Response::ConfirmIntent.new(params).to_lex
|
25
|
+
end
|
26
|
+
|
27
|
+
def delegate(opts = {})
|
28
|
+
params = {
|
29
|
+
session_attributes: lex.session_attributes,
|
30
|
+
recent_intent_summary_view: lex.recent_intent_summary_view,
|
31
|
+
slots: lex.current_intent.slots
|
32
|
+
}.merge(opts)
|
33
|
+
Response::Delegate.new(params).to_lex
|
34
|
+
end
|
35
|
+
|
36
|
+
def elicit_intent(opts = {})
|
37
|
+
params = {
|
38
|
+
session_attributes: lex.session_attributes,
|
39
|
+
recent_intent_summary_view: lex.recent_intent_summary_view
|
40
|
+
}.merge(opts)
|
41
|
+
Response::ElicitIntent.new(params).to_lex
|
42
|
+
end
|
43
|
+
|
44
|
+
def elicit_slot(opts = {})
|
45
|
+
params = {
|
46
|
+
session_attributes: lex.session_attributes,
|
47
|
+
recent_intent_summary_view: lex.recent_intent_summary_view,
|
48
|
+
slots: lex.current_intent.slots,
|
49
|
+
intent_name: lex.current_intent.name
|
50
|
+
}.merge(opts)
|
51
|
+
Response::ElicitSlot.new(params).to_lex
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Aws
|
4
|
+
module Lex
|
5
|
+
class Conversation
|
6
|
+
module Support
|
7
|
+
module Mixins
|
8
|
+
module SlotElicitation
|
9
|
+
def self.included(base)
|
10
|
+
base.include(InstanceMethods)
|
11
|
+
base.extend(ClassMethods)
|
12
|
+
base.attr_accessor(:conversation)
|
13
|
+
base.class_attribute(:slots_to_elicit)
|
14
|
+
end
|
15
|
+
|
16
|
+
module InstanceMethods
|
17
|
+
def elicit_slots!
|
18
|
+
slot_elicitor.elicit!
|
19
|
+
end
|
20
|
+
|
21
|
+
def slots_elicitable?
|
22
|
+
slot_elicitor.elicit?
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def slot_elicitor
|
28
|
+
@slot_elicitor ||= Aws::Lex::Conversation::Slot::Elicitor.new(
|
29
|
+
conversation: conversation,
|
30
|
+
elicitations: self.class.slots_to_elicit
|
31
|
+
)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
module ClassMethods
|
36
|
+
def slot(opts = {})
|
37
|
+
self.slots_to_elicit ||= []
|
38
|
+
self.slots_to_elicit.push(
|
39
|
+
Aws::Lex::Conversation::Slot::Elicitation.new(opts)
|
40
|
+
)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -19,7 +19,8 @@ module Aws
|
|
19
19
|
|
20
20
|
module InstanceMethods
|
21
21
|
def assign_attributes!(opts = {})
|
22
|
-
self.class.attributes
|
22
|
+
attributes = self.class.attributes | self.class.virtual_attributes
|
23
|
+
attributes.each do |attribute|
|
23
24
|
instance_variable_set("@#{attribute}", opts[attribute])
|
24
25
|
end
|
25
26
|
end
|
@@ -60,6 +61,17 @@ module Aws
|
|
60
61
|
->(v) { v.transform_keys(&:to_sym) }
|
61
62
|
end
|
62
63
|
|
64
|
+
def computed_property(attribute, callable)
|
65
|
+
mapping[attribute] = attribute
|
66
|
+
attr_writer(attribute)
|
67
|
+
|
68
|
+
# dynamically memoize the result
|
69
|
+
define_method(attribute) do
|
70
|
+
instance_variable_get("@#{attribute}") ||
|
71
|
+
instance_variable_set("@#{attribute}", callable.call(self))
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
63
75
|
def required(attribute, opts = {})
|
64
76
|
property(attribute, opts.merge(allow_nil: false))
|
65
77
|
end
|
@@ -76,7 +88,12 @@ module Aws
|
|
76
88
|
|
77
89
|
attr_accessor(attribute)
|
78
90
|
|
79
|
-
|
91
|
+
if opts.fetch(:virtual) { false }
|
92
|
+
virtual_attributes << attribute
|
93
|
+
else
|
94
|
+
mapping[attribute] = from
|
95
|
+
end
|
96
|
+
|
80
97
|
translate(attribute => params)
|
81
98
|
end
|
82
99
|
|
@@ -84,6 +101,10 @@ module Aws
|
|
84
101
|
@attributes ||= mapping.keys
|
85
102
|
end
|
86
103
|
|
104
|
+
def virtual_attributes
|
105
|
+
@virtual_attributes ||= []
|
106
|
+
end
|
107
|
+
|
87
108
|
def mapping
|
88
109
|
@mapping ||= {}
|
89
110
|
end
|
@@ -8,22 +8,35 @@ module Aws
|
|
8
8
|
include Base
|
9
9
|
|
10
10
|
required :name
|
11
|
-
required :slots
|
11
|
+
required :raw_slots, from: :slots, virtual: true
|
12
12
|
required :slot_details
|
13
13
|
required :confirmation_status
|
14
14
|
|
15
|
+
computed_property :slots, ->(instance) do
|
16
|
+
instance.raw_slots.each_with_object({}) do |(key, value), hash|
|
17
|
+
hash[key.to_sym] = Slot.shrink_wrap(
|
18
|
+
name: key,
|
19
|
+
value: value,
|
20
|
+
# pass a reference to the parent down to the slot so that each slot
|
21
|
+
# instance can view a broader scope such as slot_details/resolutions
|
22
|
+
current_intent: instance
|
23
|
+
)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
15
27
|
class << self
|
16
28
|
def slot_details!
|
17
|
-
->(
|
18
|
-
|
19
|
-
|
20
|
-
|
29
|
+
->(val) do
|
30
|
+
val
|
31
|
+
.reject { |_, v| v.nil? }
|
32
|
+
.each_with_object({}) do |(key, value), hash|
|
33
|
+
hash[key.to_sym] = SlotDetail.shrink_wrap(value)
|
34
|
+
end
|
21
35
|
end
|
22
36
|
end
|
23
37
|
end
|
24
38
|
|
25
39
|
coerce(
|
26
|
-
slots: symbolize_hash!,
|
27
40
|
slot_details: slot_details!,
|
28
41
|
confirmation_status: ConfirmationStatus
|
29
42
|
)
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Aws
|
4
|
+
module Lex
|
5
|
+
class Conversation
|
6
|
+
module Type
|
7
|
+
class Slot
|
8
|
+
include Base
|
9
|
+
|
10
|
+
required :current_intent, from: :current_intent, virtual: true
|
11
|
+
required :name
|
12
|
+
required :value
|
13
|
+
|
14
|
+
def as_json(_opts = {})
|
15
|
+
to_lex
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_lex
|
19
|
+
value
|
20
|
+
end
|
21
|
+
|
22
|
+
def filled?
|
23
|
+
value.to_s != ''
|
24
|
+
end
|
25
|
+
|
26
|
+
def resolve!(index: 0)
|
27
|
+
self.value = resolved(index: index)
|
28
|
+
end
|
29
|
+
|
30
|
+
def resolved(index: 0)
|
31
|
+
details.resolutions.fetch(index) { SlotResolution.new(value: value) }.value
|
32
|
+
end
|
33
|
+
|
34
|
+
def original_value
|
35
|
+
details.original_value
|
36
|
+
end
|
37
|
+
|
38
|
+
def resolvable?
|
39
|
+
details.resolutions.any?
|
40
|
+
end
|
41
|
+
|
42
|
+
def details
|
43
|
+
@details ||= current_intent.slot_details.fetch(name.to_sym) do
|
44
|
+
SlotDetail.new(name: name, resolutions: [], original_value: value)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
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: 1.2.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-07-22 00:00:00.000000000 Z
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
18
18
|
name: shrink_wrap
|
@@ -64,8 +64,11 @@ files:
|
|
64
64
|
- lib/aws/lex/conversation/response/delegate.rb
|
65
65
|
- lib/aws/lex/conversation/response/elicit_intent.rb
|
66
66
|
- lib/aws/lex/conversation/response/elicit_slot.rb
|
67
|
+
- lib/aws/lex/conversation/slot/elicitation.rb
|
68
|
+
- lib/aws/lex/conversation/slot/elicitor.rb
|
67
69
|
- lib/aws/lex/conversation/support/inflector.rb
|
68
|
-
- lib/aws/lex/conversation/support/responses.rb
|
70
|
+
- lib/aws/lex/conversation/support/mixins/responses.rb
|
71
|
+
- lib/aws/lex/conversation/support/mixins/slot_elicitation.rb
|
69
72
|
- lib/aws/lex/conversation/type/base.rb
|
70
73
|
- lib/aws/lex/conversation/type/bot.rb
|
71
74
|
- lib/aws/lex/conversation/type/confirmation_status.rb
|
@@ -87,6 +90,7 @@ files:
|
|
87
90
|
- lib/aws/lex/conversation/type/sentiment_label.rb
|
88
91
|
- lib/aws/lex/conversation/type/sentiment_response.rb
|
89
92
|
- lib/aws/lex/conversation/type/sentiment_score.rb
|
93
|
+
- lib/aws/lex/conversation/type/slot.rb
|
90
94
|
- lib/aws/lex/conversation/type/slot_detail.rb
|
91
95
|
- lib/aws/lex/conversation/type/slot_resolution.rb
|
92
96
|
- lib/aws/lex/conversation/version.rb
|
@@ -1,56 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Aws
|
4
|
-
module Lex
|
5
|
-
class Conversation
|
6
|
-
module Support
|
7
|
-
module Responses
|
8
|
-
def close(opts = {})
|
9
|
-
params = {
|
10
|
-
session_attributes: lex.session_attributes,
|
11
|
-
recent_intent_summary_view: lex.recent_intent_summary_view
|
12
|
-
}.merge(opts)
|
13
|
-
Response::Close.new(params).to_lex
|
14
|
-
end
|
15
|
-
|
16
|
-
def confirm_intent(opts = {})
|
17
|
-
params = {
|
18
|
-
session_attributes: lex.session_attributes,
|
19
|
-
recent_intent_summary_view: lex.recent_intent_summary_view,
|
20
|
-
intent_name: lex.current_intent.name,
|
21
|
-
slots: lex.current_intent.slots
|
22
|
-
}.merge(opts)
|
23
|
-
Response::ConfirmIntent.new(params).to_lex
|
24
|
-
end
|
25
|
-
|
26
|
-
def delegate(opts = {})
|
27
|
-
params = {
|
28
|
-
session_attributes: lex.session_attributes,
|
29
|
-
recent_intent_summary_view: lex.recent_intent_summary_view,
|
30
|
-
slots: lex.current_intent.slots
|
31
|
-
}.merge(opts)
|
32
|
-
Response::Delegate.new(params).to_lex
|
33
|
-
end
|
34
|
-
|
35
|
-
def elicit_intent(opts = {})
|
36
|
-
params = {
|
37
|
-
session_attributes: lex.session_attributes,
|
38
|
-
recent_intent_summary_view: lex.recent_intent_summary_view
|
39
|
-
}.merge(opts)
|
40
|
-
Response::ElicitIntent.new(params).to_lex
|
41
|
-
end
|
42
|
-
|
43
|
-
def elicit_slot(opts = {})
|
44
|
-
params = {
|
45
|
-
session_attributes: lex.session_attributes,
|
46
|
-
recent_intent_summary_view: lex.recent_intent_summary_view,
|
47
|
-
slots: lex.current_intent.slots,
|
48
|
-
intent_name: lex.current_intent.name
|
49
|
-
}.merge(opts)
|
50
|
-
Response::ElicitSlot.new(params).to_lex
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|