aws-lex-conversation 0.5.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|