aws-lex-conversation 6.3.0 → 6.4.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: 33df2c318e3e0d2e3157ebe43e27b38855334518efa5fa957be0e1035e6454f2
4
- data.tar.gz: 5f7db4ab55ef155967688aae6a85781b1fca26860574d45280cfceb2e410b730
3
+ metadata.gz: 61c4edf66e617becb5f2c7aa3611e32c7eeef58ab2cb9533f1816abcaf675ab8
4
+ data.tar.gz: 485e85d4c07d25527752e2451e99c02af1eae808dfd9e048efa147a8042d76aa
5
5
  SHA512:
6
- metadata.gz: 4831b1eed996b95384635c446a2432efbb765900a8ef814c089ab4bbbfa5311708b0f71fe080d5b4910110448c698dfe855747bc2d9b9a8b19643743581b9c86
7
- data.tar.gz: 53cdad241f6ace96a3ea1cb7be904f3518edbab40a403c8ab0641ea26cc6629ae1e0ce16b642cf08217a6f06017936e5865ac01be74e3fdeb620f4654edad52c
6
+ metadata.gz: 03f8814a82e0f6cea94033ef13d4f59ade4bb74a1cee26fe554151bff1b0785f32f0c3aef17e2a2293a1c71dd4442679ee516a9d16bd1bc94069e0e00dccfb3f
7
+ data.tar.gz: 360cb550a21e90e6063722c676071ed59d083e6a03eb44281f2517b79b8e2a70ccfeb435ad0df4b37b1c46a8e6e9e4258505622b6396f3168a140eb9e2998524
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ # 6.4.0 - Feb 10, 2022
2
+
3
+ * Add support for the new protocol properties of `proposedNextState` and `transcriptions`. This enables support for [transcription confidence scores](https://docs.aws.amazon.com/lexv2/latest/dg/using-transcript-confidence-scores.html).
4
+ * Expose the following methods on `Aws::Lex::Conversation` instances:
5
+ - `proposed_next_state?`: Returns `true` if a `proposedNextState` value is present or `false` otherwise.
6
+ - `proposed_next_state`: Returns an `Aws::Lex::Conversation::Type::ProposedNextState` instance. May return `nil`.
7
+ - `transcriptions`: Returns an array of `Aws::Lex::Conversation::Type::Transcription` instances. Defaults to a empty array if the `transcriptions` property is not present.
8
+ * Make the `content_type` attribute in the constructor for `Aws::Lex::Conversation::Type::Message` default to `PlainText` when not present.
9
+
1
10
  # 6.3.0 - Nov 22, 2021
2
11
 
3
12
  * Add support for the recently added `slotElicitationStyle` property when generating an `ElicitSlot` repsonse ([documentation](https://docs.aws.amazon.com/lexv2/latest/dg/using-spelling.html)).
data/README.md CHANGED
@@ -1,19 +1,15 @@
1
- # Aws::Lex::Conversation
1
+ # `Aws::Lex::Conversation`
2
2
 
3
- Have you played around with [AWS Lex](https://aws.amazon.com/lex/) and quickly realized you were duplicating code to read and generate the [Lex Lambda input event and response format](https://docs.aws.amazon.com/lex/latest/dg/lambda-input-response-format.html)?
3
+ Building a chatbot with [AWS Lex](https://aws.amazon.com/lex/) is really fun! Unfortunately implementing your bot's behaviour with the [Lex/Lambda event protocol](https://docs.aws.amazon.com/lexv2/latest/dg/lambda.html) is less fun.
4
4
 
5
- `Aws::Lex::Conversation` provides a mechanism to define the flow of a Lex conversation with a user easily!
5
+ But don't worry - we've done the hard work for you!
6
6
 
7
- ## Installation
8
-
9
- ### Lex V1
10
-
11
- Version 3.x is the last major version of this gem that will support Lex V1.
7
+ `Aws::Lex::Conversation` makes it simple to build dynamic, conversational chatbots with AWS Lex and AWS Lambda!
12
8
 
13
- Add this line to your application's Gemfile:
9
+ ## Installation
14
10
 
15
11
  ```ruby
16
- gem 'aws-lex-conversation', '~> 3.0'
12
+ gem 'aws-lex-conversation'
17
13
  ```
18
14
 
19
15
  And then execute:
@@ -22,96 +18,162 @@ And then execute:
22
18
  bundle install
23
19
  ```
24
20
 
25
- ### Lex V2
21
+ **Please Note:** This library currently supports [AWS Lex Version 2](https://docs.aws.amazon.com/lexv2/latest/dg/lambda.html). If you're looking for Lex V1 support, lock `aws-lex-conversation` to `~> 3.0` in your `Gemfile`.
22
+
23
+ ## Core Concepts
24
+
25
+ ### The Conversation Instance
26
26
 
27
- Version 4.x and higher support Lex V2.
27
+ Instances of `Aws::Lex::Conversation` wrap the Lex input/output event format and make it easy to manage conversation dialog.
28
28
 
29
- Add this line to your application's Gemfile:
29
+ Imagine you have a `ButlerBot` configured with a `ServeBreakfast` intent and `BreakfastFood` slot.
30
+
31
+ The backing lambda handler for your bot might look something like:
30
32
 
31
33
  ```ruby
32
- gem 'aws-lex-conversation', '>= 4.0'
34
+ require "aws-lex-conversation"
35
+
36
+ def lambda_handler(event:, context:)
37
+ # The conversation instance validates and wraps the Lex input event
38
+ conversation = Aws::Lex::Conversation.new(event: event, context: context)
39
+
40
+ # Return a Close dialog action to our Lex bot and end the conversation
41
+ conversation.close(
42
+ fulfillment_state: "Fulfilled",
43
+ messages: [
44
+ # We can construct response messages using wrapper classes.
45
+ Aws::Lex::Conversation::Type::Message.new(
46
+ content: "Hi - I'm a 🤖!"
47
+ ),
48
+ # Or we can pass a Hash that directly maps to the Lex response format
49
+ {
50
+ content: "Your intent is: #{conversation.intent_name}",
51
+ contentType: "PlainText"
52
+ },
53
+ {
54
+ content: "Here's your #{conversation.slots[:BreakfastFood].value}!",
55
+ contentType: "PlainText"
56
+ }
57
+ ]
58
+ )
59
+ end
33
60
  ```
34
61
 
35
- And then execute:
62
+ This lambda handler would generate the following dialog:
36
63
 
37
- ```bash
38
- bundle install
64
+ ![ButlerBot Dialog](https://raw.github.com/amaabca/aws-lex-conversation/master/data/butler_bot.png)
65
+
66
+ All data from the Lex input event is exposed via the `lex` attribute. By convention, the `lex` attribute directly translates input event attributes from `camelCase` to `snake_case`.
67
+
68
+ We also provide some helpers to help manage the conversation. For example:
69
+
70
+ ```ruby
71
+ # returns true if the lambda function was invoked as a DialogCodeHook
72
+ conversation.lex.invocation_source.dialog_code_hook?
73
+
74
+ # returns true if the InputMode is Speech
75
+ conversation.lex.input_mode.speech?
76
+
77
+ # you can easily set or retrieve a session values
78
+ conversation.session[:name] = "Jane"
79
+ conversation.session[:name] # => "Jane"
80
+
81
+ # dealing with slot data is simple
82
+ conversation.slots[:BreakfastFood].filled? # returns true if a slot value is present
83
+ conversation.slots[:Hometown].blank? # returns true if a slot value is nil/empty
84
+ conversation.slots[:FirstName].value # => "John"
39
85
  ```
40
86
 
41
- ## Usage
87
+ ### The Handler Chain
88
+
89
+ Conversational dialog gets complex quickly! Conversation instances include a handler chain that can help manage this complexity.
42
90
 
43
- At a high level, you must create a new instance of `Aws::Lex::Conversation`.
91
+ Each handler in the chain defines the prerequisites necessary for the handler to generate a response.
44
92
 
45
- Generally the conversation instance will be initialized inside your Lambda handler method as follows:
93
+ You can configure the handler chain as follows:
46
94
 
47
95
  ```ruby
48
- def my_lambda_handler(event:, context:)
96
+ def lambda_handler(event:, context:)
49
97
  conversation = Aws::Lex::Conversation.new(event: event, context: context)
50
98
 
51
- # define a chain of your own Handler classes
52
99
  conversation.handlers = [
100
+ # You need to define custom handler classes yourself
101
+ { handler: ServeBreakfast },
102
+ { handler: FallbackIntent },
103
+ # We offer a few "built in" handlers
53
104
  {
54
105
  handler: Aws::Lex::Conversation::Handler::Delegate,
55
106
  options: {
56
- respond_on: ->(conversation) { conversation.current_intent.name == 'MyIntent' }
107
+ # If we get this far, always return a Delegate action
108
+ respond_on: ->(_) { true }
57
109
  }
58
110
  }
59
111
  ]
60
112
 
61
- # return a response object to indicate Lex's next action
113
+ # The respond method will execute each handler sequentially and return a Lex response
62
114
  conversation.respond
63
115
  end
64
116
  ```
65
117
 
66
- Any custom behaviour in your flow is achieved by defining a Handler class. Handler classes must provide the following:
118
+ ### Writing Your Own Handler
119
+
120
+ Generally, custom behaviour in your flow is achieved by defining your own handler class. Handler classes must:
67
121
 
68
122
  1. Inherit from `Aws::Lex::Conversation::Handler::Base`.
69
123
  2. Define a `will_respond?(conversation)` method that returns a boolean value.
70
124
  3. Define a `response(conversation)` method to return final response to Lex. This method is called if `will_respond?` returns `true`.
71
125
 
72
- The handlers defined on an `Aws::Lex::Conversation` instance will be called one-by-one in the order defined.
126
+ Handlers in the chain are invoked sequentially in the order defined.
73
127
 
74
- The first handler that returns `true` for the `will_respond?` method will provide the final Lex response action.
128
+ The first handler in the chain that returns `true` for the `will_respond?` method will provide the final Lex response action.
75
129
 
76
- ### Custom Handler Example
130
+ Here's an example for the `ServeBreakfast` and `FallbackIntent` handlers above:
77
131
 
78
132
  ```ruby
79
- class SayHello < Aws::Lex::Conversation::Handler::Base
133
+ class ServeBreakfast < Aws::Lex::Conversation::Handler::Base
80
134
  def will_respond?(conversation)
81
- conversation.lex.invocation_source.dialog_code_hook? && # callback is for DialogCodeHook (i.e. validation)
82
- conversation.lex.current_intent.name == 'SayHello' && # Lex has routed to the 'SayHello' intent
83
- conversation.slots[:name].filled? # our expected slot value is set
135
+ conversation.intent_name == "ServeBreakfast" &&
136
+ conversation.slots[:BreakfastFood].filled?
84
137
  end
85
138
 
86
139
  def response(conversation)
87
- name = conversation.slots[:name].value
88
-
89
- # NOTE: you can use the Type::* classes if you wish. The final output
90
- # will be normalized to a value that complies with the Lex response format.
91
- #
92
- # You can also specify raw values for the response:
93
- #
94
- # conversation.close(
95
- # fulfillment_state: 'Fulfilled',
96
- # messages: [{ content: "Hello, #{name}!", contentType: 'PlainText' }]
97
- # )
98
- #
140
+ food = conversation.slots[:BreakfastFood].value
141
+ emoji = food == "waffle" ? "🧇" : "🥓"
142
+
99
143
  conversation.close(
100
- fulfillment_state: Aws::Lex::Conversation::Type::FulfillmentState.new('Fulfilled'),
144
+ fulfillment_state: "Fulfilled",
101
145
  messages: [
102
- Aws::Lex::Conversation::Type::Message.new(
103
- content: "Hello, #{name}!",
104
- content_type: Aws::Lex::Conversation::Type::Message::ContentType.new('PlainText')
105
- )
146
+ {
147
+ content: "Here's your #{emoji}!",
148
+ contentType: "PlainText"
149
+ }
150
+ ]
151
+ )
152
+ end
153
+ end
154
+
155
+ class FallbackIntent < Aws::Lex::Conversation::Handler::Base
156
+ def will_respond?(conversation)
157
+ conversation.intent_name == "FallbackIntent"
158
+ end
159
+
160
+ def response(conversation)
161
+ conversation.close(
162
+ fulfillment_state: 'Failed',
163
+ messages: [
164
+ {
165
+ content: "Sorry - I'm not sure what you said!",
166
+ contentType: "PlainText"
167
+ }
106
168
  ]
107
169
  )
108
170
  end
109
171
  end
110
172
  ```
111
173
 
112
- ## Built-In Handlers
174
+ ### Built-In Handlers
113
175
 
114
- ### `Aws::Lex::Conversation::Handler::Echo`
176
+ #### `Aws::Lex::Conversation::Handler::Echo`
115
177
 
116
178
  This handler simply returns a close response with a message that matches the `inputTranscript` property of the input event.
117
179
 
@@ -140,7 +202,7 @@ conversation.handlers = [
140
202
  conversation.respond # => { dialogAction: { type: 'Close' } ... }
141
203
  ```
142
204
 
143
- ### `Aws::Lex::Conversation::Handler::Delegate`
205
+ #### `Aws::Lex::Conversation::Handler::Delegate`
144
206
 
145
207
  This handler returns a `Delegate` response to the Lex bot (i.e. "do the next bot action").
146
208
 
@@ -163,7 +225,7 @@ conversation.handlers = [
163
225
  conversation.respond # => { dialogAction: { type: 'Delegate' } }
164
226
  ```
165
227
 
166
- ### `Aws::Lex::Conversation::Handler::SlotResolution`
228
+ #### `Aws::Lex::Conversation::Handler::SlotResolution`
167
229
 
168
230
  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.
169
231
 
@@ -359,8 +421,6 @@ end
359
421
 
360
422
  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.
361
423
 
362
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
363
-
364
424
  ## Contributing
365
425
 
366
426
  Bug reports and pull requests are welcome on GitHub at https://github.com/amaabca/aws-lex-conversation. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/amaabca/aws-lex-conversation/blob/master/CODE_OF_CONDUCT.md).
@@ -371,4 +431,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
371
431
 
372
432
  ## Code of Conduct
373
433
 
374
- Everyone interacting in the Aws::Lex::Conversation project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/amaabca/aws-lex-conversation/blob/master/CODE_OF_CONDUCT.md).
434
+ Everyone interacting in the `Aws::Lex::Conversation` project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/amaabca/aws-lex-conversation/blob/master/CODE_OF_CONDUCT.md).
@@ -29,7 +29,7 @@ Gem::Specification.new do |spec|
29
29
  spec.metadata['rubygems_mfa_required'] = 'true'
30
30
 
31
31
  spec.files = Dir.chdir(File.expand_path(__dir__)) do
32
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
32
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|data)/}) }
33
33
  end
34
34
 
35
35
  spec.bindir = 'exe'
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'base64'
4
+ require 'forwardable'
4
5
  require 'json'
5
6
  require 'shrink/wrap'
6
7
 
@@ -36,11 +37,14 @@ require_relative 'type/slot_value'
36
37
  require_relative 'type/slot'
37
38
  require_relative 'type/context'
38
39
  require_relative 'type/intent'
40
+ require_relative 'type/proposed_next_state'
39
41
  require_relative 'type/checkpoint'
40
42
  require_relative 'type/session_attributes'
41
43
  require_relative 'type/session_state'
42
44
  require_relative 'type/interpretation'
43
45
  require_relative 'type/bot'
46
+ require_relative 'type/transcription/resolved_context'
47
+ require_relative 'type/transcription'
44
48
  require_relative 'type/response_card/button'
45
49
  require_relative 'type/response_card'
46
50
  require_relative 'type/message/content_type'
@@ -120,13 +120,41 @@ module Aws
120
120
  self
121
121
  end
122
122
 
123
+ def proposed_next_state(opts = {})
124
+ data = {
125
+ dialogAction: lex_attributes(opts.fetch(:dialog_action)),
126
+ intent: lex_attributes(opts.fetch(:intent))
127
+ }
128
+ lex.proposed_next_state = Type::ProposedNextState.shrink_wrap(data)
129
+ self
130
+ end
131
+
123
132
  def session(data)
124
133
  lex.session_state.session_attributes.merge!(Type::SessionAttributes[data])
125
134
  self
126
135
  end
127
136
 
137
+ def transcription(opts = {})
138
+ data = {
139
+ transcription: opts.fetch(:transcription),
140
+ transcriptionConfidence: opts.fetch(:confidence, 1),
141
+ resolvedContext: {
142
+ intent: opts.fetch(:intent) { 'FallbackIntent' }
143
+ },
144
+ resolvedSlots: opts.fetch(:resolved_slots) { {} }
145
+ }
146
+ lex.transcriptions.push(Type::Transcription.shrink_wrap(data))
147
+ self
148
+ end
149
+
128
150
  private
129
151
 
152
+ def lex_attributes(instance)
153
+ return instance unless instance.respond_to?(:to_lex)
154
+
155
+ instance.to_lex
156
+ end
157
+
130
158
  def current_interpretation
131
159
  lex.interpretations.find { |i| i.intent.name == lex.session_state.intent.name }
132
160
  end
@@ -17,6 +17,8 @@ module Aws
17
17
  required :response_content_type
18
18
  required :session_id
19
19
  required :session_state
20
+ required :transcriptions, default: -> { [] }
21
+ optional :proposed_next_state
20
22
 
21
23
  computed_property(:current_intent, virtual: true) do |instance|
22
24
  instance.session_state.intent.tap do |intent|
@@ -41,9 +43,11 @@ module Aws
41
43
  input_mode: InputMode,
42
44
  interpretations: Array[Interpretation],
43
45
  invocation_source: InvocationSource,
46
+ proposed_next_state: ProposedNextState,
44
47
  request_attributes: symbolize_hash!,
45
48
  response_content_type: Message::ContentType,
46
- session_state: SessionState
49
+ session_state: SessionState,
50
+ transcriptions: Array[Transcription]
47
51
  )
48
52
  end
49
53
  end
@@ -15,6 +15,11 @@ module Aws
15
15
  content_type: Message::ContentType,
16
16
  image_response_card: Aws::Lex::Conversation::Type::ResponseCard
17
17
  )
18
+
19
+ def initialize(opts = {})
20
+ assign_attributes!(opts)
21
+ self.content_type ||= ContentType.new('PlainText')
22
+ end
18
23
  end
19
24
  end
20
25
  end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aws
4
+ module Lex
5
+ class Conversation
6
+ module Type
7
+ class ProposedNextState
8
+ include Base
9
+
10
+ required :dialog_action
11
+ required :intent
12
+
13
+ coerce(
14
+ dialog_action: DialogAction,
15
+ intent: Intent
16
+ )
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aws
4
+ module Lex
5
+ class Conversation
6
+ module Type
7
+ class Transcription
8
+ class ResolvedContext
9
+ include Base
10
+
11
+ required :intent
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aws
4
+ module Lex
5
+ class Conversation
6
+ module Type
7
+ class Transcription
8
+ include Base
9
+
10
+ required :transcription
11
+ required :transcription_confidence
12
+ optional :resolved_context
13
+ optional :resolved_slots
14
+ alias_method :confidence, :transcription_confidence
15
+
16
+ coerce(
17
+ resolved_context: Transcription::ResolvedContext,
18
+ resolved_slots: Array[Slot]
19
+ )
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -3,7 +3,7 @@
3
3
  module Aws
4
4
  module Lex
5
5
  class Conversation
6
- VERSION = '6.3.0'
6
+ VERSION = '6.4.0'
7
7
  end
8
8
  end
9
9
  end
@@ -5,10 +5,13 @@ require_relative 'conversation/base'
5
5
  module Aws
6
6
  module Lex
7
7
  class Conversation
8
+ extend Forwardable
8
9
  include Support::Mixins::Responses
9
10
 
10
11
  attr_accessor :event, :context, :lex
11
12
 
13
+ def_delegators :@lex, :proposed_next_state, :transcriptions
14
+
12
15
  def initialize(opts = {})
13
16
  self.event = opts.fetch(:event)
14
17
  self.context = opts.fetch(:context)
@@ -142,6 +145,10 @@ module Aws
142
145
  lex.session_state.active_contexts = []
143
146
  end
144
147
 
148
+ def proposed_next_state?
149
+ !proposed_next_state.nil?
150
+ end
151
+
145
152
  def stash
146
153
  @stash ||= {}
147
154
  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: 6.3.0
4
+ version: 6.4.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: 2021-11-22 00:00:00.000000000 Z
15
+ date: 2022-02-10 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: shrink_wrap
@@ -92,6 +92,7 @@ files:
92
92
  - lib/aws/lex/conversation/type/invocation_source.rb
93
93
  - lib/aws/lex/conversation/type/message.rb
94
94
  - lib/aws/lex/conversation/type/message/content_type.rb
95
+ - lib/aws/lex/conversation/type/proposed_next_state.rb
95
96
  - lib/aws/lex/conversation/type/response.rb
96
97
  - lib/aws/lex/conversation/type/response_card.rb
97
98
  - lib/aws/lex/conversation/type/response_card/button.rb
@@ -105,6 +106,8 @@ files:
105
106
  - lib/aws/lex/conversation/type/slot_shape.rb
106
107
  - lib/aws/lex/conversation/type/slot_value.rb
107
108
  - lib/aws/lex/conversation/type/time_to_live.rb
109
+ - lib/aws/lex/conversation/type/transcription.rb
110
+ - lib/aws/lex/conversation/type/transcription/resolved_context.rb
108
111
  - lib/aws/lex/conversation/version.rb
109
112
  homepage: https://github.com/amaabca/aws-lex-conversation
110
113
  licenses: