wit 4.1.0 → 5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 79a8e68599e29e35430b0cae7ed26691435dbd38
4
- data.tar.gz: 5934e10cb348235ab7f619ef6030d5e1e4bce06d
3
+ metadata.gz: a8b09beea4b5f87b4ee4ac6db91a81e302d09415
4
+ data.tar.gz: 82cd100fe0cb84a361aa96afc8295e2e577afb0d
5
5
  SHA512:
6
- metadata.gz: a61fc72b671a939e0970487825ee000743ba14d3537f002e9455fc9841ea965db400294c5f230aefa6d7aef288e4f883a38647ecc32ebd3d4bc4c59e3e760904
7
- data.tar.gz: 1b9c4d937a90aafc671282e88a5444b463fc6c71e7f81235f5084c0543813cea1ebf9ec57abfd7f1162959c2b5d2e1d881f99fe4b76f1fa667c29ac7a6f4f486
6
+ metadata.gz: 13b1c151a243c6c6b3a6c1d2b4104cf8217279989360da423b625435781e04171e8ed848e7b66565438270ae25e9f66868f0e91dc8ca6487306f0bb20d55a464
7
+ data.tar.gz: 924f723edc7da68ceac7cd6287cdd229fee6763050cf754360b434ac2deedcee66f64dc988ab4557940b0cc7fad7be675bb6c1d3e37a1f9de2497c04b3df3ef2
data/CHANGES.md CHANGED
@@ -1,3 +1,14 @@
1
+ ## v5.0.0
2
+
3
+ - `converse` and `run_actions` are deprecated
4
+ - `interactive` now calls `message`
5
+
6
+ ### Breaking changes
7
+ - Renamed WitException to Wit::Error
8
+ - Changed Wit::Error to inherit from StandardError instead of Exception
9
+ - Moved constants inside Wit namespace
10
+ - Moved #req and #validate_actions to private methods within Wit namespace
11
+
1
12
  ## v4.1.0
2
13
 
3
14
  - `converse` now takes `reset` as optional parameter.
data/README.md CHANGED
@@ -32,33 +32,19 @@ See the `examples` folder for more examples.
32
32
 
33
33
  `wit-ruby` provides a Wit class with the following methods:
34
34
  * `message` - the Wit [message API](https://wit.ai/docs/http/20160330#get-intent-via-text-link)
35
- * `converse` - the low-level Wit [converse API](https://wit.ai/docs/http/20160330#converse-link)
36
- * `run_actions` - a higher-level method to the Wit converse API
37
35
  * `interactive` - starts an interactive conversation with your bot
38
36
 
39
37
  ### Wit class
40
38
 
41
39
  The Wit constructor takes a `Hash` with the following symbol keys:
42
40
  * `:access_token` - the access token of your Wit instance
43
- * `:actions` - the `Hash` with your actions
44
-
45
- The `actions` `Hash` has action names as keys, and action implementations as values.
46
- Action names are symbols, and action implementations are lambda functions (not `Proc`).
47
41
 
48
42
  A minimal example looks like this:
49
43
  ```ruby
50
44
  require 'wit'
51
45
 
52
- actions = {
53
- send: -> (request, response) {
54
- puts("sending... #{response['text']}")
55
- },
56
- my_action: -> (request) {
57
- return request['context']
58
- },
59
- }
60
-
61
- client = Wit.new(access_token: access_token, actions: actions)
46
+ client = Wit.new(access_token: access_token)
47
+ client.message('set an alarm tomorrow at 7am')
62
48
  ```
63
49
 
64
50
  ### .message()
@@ -74,8 +60,19 @@ rsp = client.message('what is the weather in London?')
74
60
  puts("Yay, got Wit.ai response: #{rsp}")
75
61
  ```
76
62
 
63
+ ### .interactive()
64
+
65
+ Starts an interactive conversation with your bot.
66
+
67
+ Example:
68
+ ```ruby
69
+ client.interactive
70
+ ```
71
+
77
72
  ### .run_actions()
78
73
 
74
+ **DEPRECATED** See [our blog post](https://wit.ai/blog) for a migration plan.
75
+
79
76
  A higher-level method to the Wit converse API.
80
77
  `run_actions` resets the last turn on new messages and errors.
81
78
 
@@ -97,6 +94,8 @@ p "The session state is now: #{context2}"
97
94
 
98
95
  ### .converse()
99
96
 
97
+ **DEPRECATED** See [our blog post](https://wit.ai/blog) for a migration plan.
98
+
100
99
  The low-level Wit [converse API](https://wit.ai/docs/http/20160330#converse-link).
101
100
 
102
101
  Takes the following parameters:
@@ -111,14 +110,44 @@ rsp = client.converse('my-user-session-42', 'what is the weather in London?', {}
111
110
  puts("Yay, got Wit.ai response: #{rsp}")
112
111
  ```
113
112
 
114
- ### .interactive()
113
+ ### CRUD operations for entities
114
+ payload in the parameters is a hash containing API arguments
115
115
 
116
- Starts an interactive conversation with your bot.
116
+ #### .get_entities()
117
+ Returns a list of available entities for the app.
118
+ See [GET /entities](https://wit.ai/docs/http/20160526#get--entities-link)
117
119
 
118
- Example:
119
- ```ruby
120
- client.interactive
121
- ```
120
+ #### .post_entities(payload)
121
+ Creates a new entity with the given attributes.
122
+ See [POST /entities](https://wit.ai/docs/http/20160526#post--entities-link)
123
+
124
+ #### .get_entity(entity_id)
125
+ Returns all the expressions validated for an entity.
126
+ See [GET /entities/:entity-id](https://wit.ai/docs/http/20160526#get--entities-:entity-id-link)
127
+
128
+ #### .put_entities(entity_id, payload)
129
+ Updates an entity with the given attributes.
130
+ See [PUT /entities/:entity-id](https://wit.ai/docs/http/20160526#put--entities-:entity-id-link)
131
+
132
+ #### .delete_entities(entity_id)
133
+ Permanently remove the entity.
134
+ See [DELETE /entities/:entity-id](https://wit.ai/docs/http/20160526#delete--entities-:entity-id-link)
135
+
136
+ #### .post_values(entity_id, payload)
137
+ Add a possible value into the list of values for the entity.
138
+ See [POST /entities/:entity-id/values](https://wit.ai/docs/http/20160526#post--entities-:entity-id-values-link)
139
+
140
+ #### .delete_values(entity_id, value)
141
+ Delete a canonical value from the entity.
142
+ See [DELETE /entities/:entity-id/values/:value](https://wit.ai/docs/http/20160526#delete--entities-:entity-id-values-link)
143
+
144
+ #### post_expressions(entity_id, value, payload)
145
+ Create a new expression of the canonical value of the entity.
146
+ See [POST /entities/:entity-id/values/:value/expressions](https://wit.ai/docs/http/20160526#post--entities-:entity-id-values-:value-id-expressions-link)
147
+
148
+ #### delete_expressions(entity_id, value, expression)
149
+ Delete an expression of the canonical value of the entity.
150
+ See [DELETE /entities/:entity-id/values/:value/expressions/:expression](https://wit.ai/docs/http/20160526#delete--entities-:entity-id-values-:value-id-expressions-link)
122
151
 
123
152
  See the [docs](https://wit.ai/docs) for more information.
124
153
 
@@ -29,6 +29,7 @@ actions = {
29
29
  loc = first_entity_value(entities, 'location')
30
30
  if loc
31
31
  context['forecast'] = 'sunny'
32
+ context.delete('missingLocation')
32
33
  else
33
34
  context['missingLocation'] = true
34
35
  context.delete('forecast')
data/lib/wit.rb CHANGED
@@ -3,64 +3,14 @@ require 'logger'
3
3
  require 'net/http'
4
4
  require 'securerandom'
5
5
 
6
- WIT_API_HOST = ENV['WIT_URL'] || 'https://api.wit.ai'
7
- WIT_API_VERSION = ENV['WIT_API_VERSION'] || '20160516'
8
- DEFAULT_MAX_STEPS = 5
9
- LEARN_MORE = 'Learn more at https://wit.ai/docs/quickstart'
10
-
11
- class WitException < Exception
12
- end
13
-
14
- def req(logger, access_token, meth_class, path, params={}, payload={})
15
- uri = URI(WIT_API_HOST + path)
16
- uri.query = URI.encode_www_form(params)
17
-
18
- logger.debug("#{meth_class} #{uri}")
19
-
20
- request = meth_class.new(uri)
21
- request['authorization'] = 'Bearer ' + access_token
22
- request['accept'] = 'application/vnd.wit.' + WIT_API_VERSION + '+json'
23
- request.add_field 'Content-Type', 'application/json'
24
- request.body = payload.to_json
6
+ class Wit
7
+ class Error < StandardError; end
25
8
 
26
- Net::HTTP.start(uri.host, uri.port, {:use_ssl => uri.scheme == 'https'}) do |http|
27
- rsp = http.request(request)
28
- if rsp.code.to_i != 200
29
- raise WitException.new("HTTP error code=#{rsp.code}")
30
- end
31
- json = JSON.parse(rsp.body)
32
- if json.has_key?('error')
33
- raise WitException.new("Wit responded with an error: #{json['error']}")
34
- end
35
- logger.debug("#{meth_class} #{uri} #{json}")
36
- json
37
- end
38
- end
9
+ WIT_API_HOST = ENV['WIT_URL'] || 'https://api.wit.ai'
10
+ WIT_API_VERSION = ENV['WIT_API_VERSION'] || '20160516'
11
+ DEFAULT_MAX_STEPS = 5
12
+ LEARN_MORE = 'Learn more at https://wit.ai/docs/quickstart'
39
13
 
40
- def validate_actions(logger, actions)
41
- [:send].each do |action|
42
- if !actions.has_key?(action)
43
- logger.warn "The #{action} action is missing. #{LEARN_MORE}"
44
- end
45
- end
46
- actions.each_pair do |k, v|
47
- if !k.is_a?(Symbol)
48
- logger.warn "The '#{k}' action name should be a symbol"
49
- end
50
- if !(v.respond_to?(:call) && v.lambda?)
51
- logger.warn "The '#{k}' action should be a lambda function"
52
- end
53
- if k == :send && v.arity != 2
54
- logger.warn "The \'send\' action should take 2 arguments: request and response. #{LEARN_MORE}"
55
- end
56
- if k != :send && v.arity != 1
57
- logger.warn "The '#{k}' action should take 1 argument: request. #{LEARN_MORE}"
58
- end
59
- end
60
- return actions
61
- end
62
-
63
- class Wit
64
14
  def initialize(opts = {})
65
15
  @access_token = opts[:access_token]
66
16
 
@@ -69,6 +19,7 @@ class Wit
69
19
  end
70
20
 
71
21
  if opts[:actions]
22
+ logger.warn('Stories and POST /converse have been deprecated. This will break in February 2018!')
72
23
  @actions = validate_actions(logger, opts[:actions])
73
24
  end
74
25
 
@@ -92,7 +43,7 @@ class Wit
92
43
 
93
44
  def converse(session_id, msg, context={}, reset=nil)
94
45
  if !context.is_a?(Hash)
95
- raise WitException.new('context should be a Hash')
46
+ raise Error.new('context should be a Hash')
96
47
  end
97
48
  params = {}
98
49
  params[:q] = msg unless msg.nil?
@@ -102,13 +53,131 @@ class Wit
102
53
  return res
103
54
  end
104
55
 
56
+ def run_actions(session_id, message, context={}, max_steps=DEFAULT_MAX_STEPS)
57
+ if !@actions
58
+ throw_must_have_actions
59
+ end
60
+ if !context.is_a?(Hash)
61
+ raise Error.new('context should be a Hash')
62
+ end
63
+
64
+ # Figuring out whether we need to reset the last turn.
65
+ # Each new call increments an index for the session.
66
+ # We only care about the last call to run_actions.
67
+ # All the previous ones are discarded (preemptive exit).
68
+ current_request = 1
69
+ if @_sessions.has_key?(session_id)
70
+ current_request = @_sessions[session_id] + 1
71
+ end
72
+ @_sessions[session_id] = current_request
73
+
74
+ context = __run_actions(session_id, current_request, message, context, max_steps)
75
+
76
+ # Cleaning up once the last call to run_actions finishes.
77
+ if current_request == @_sessions[session_id]
78
+ @_sessions.delete(session_id)
79
+ end
80
+
81
+ return context
82
+ end
83
+
84
+ def interactive(context={})
85
+ while true
86
+ print '> '
87
+ msg = STDIN.gets.strip
88
+ if msg == ''
89
+ next
90
+ end
91
+
92
+ begin
93
+ puts(message(msg))
94
+ rescue Error => exp
95
+ logger.error("error: #{exp.message}")
96
+ end
97
+ end
98
+ rescue Interrupt => _exp
99
+ puts
100
+ end
101
+
102
+ def throw_if_action_missing(action_name)
103
+ if !@actions.has_key?(action_name)
104
+ raise Error.new("unknown action: #{action_name}")
105
+ end
106
+ end
107
+
108
+ def throw_must_have_actions()
109
+ raise Error.new('You must provide the `actions` parameter to be able to use runActions. ' + LEARN_MORE)
110
+ end
111
+
112
+ def get_entities
113
+ req(logger, @access_token, Net::HTTP::Get, "/entities")
114
+ end
115
+
116
+ def post_entities(payload)
117
+ payload = payload.map {|k, v| [(k.to_sym rescue k), v]}.to_h.reject{ |k| ![:id, :doc, :values, :lookups].include?(k) }
118
+ validate_payload payload
119
+ req(logger, @access_token, Net::HTTP::Post, "/entities", {}, payload)
120
+ end
121
+
122
+ def get_entity(entity_id)
123
+ req(logger, @access_token, Net::HTTP::Get, "/entities/#{URI.encode(entity_id)}")
124
+ end
125
+
126
+ def put_entities(entity_id, payload)
127
+ payload = payload.map {|k, v| [(k.to_sym rescue k), v]}.to_h.reject{ |k| ![:id, :doc, :values].include?(k) }
128
+ validate_payload payload
129
+ req(logger, @access_token, Net::HTTP::Put, "/entities/#{URI.encode(entity_id)}", {}, payload)
130
+ end
131
+
132
+ def delete_entities(entity_id)
133
+ req(logger, @access_token, Net::HTTP::Delete, "/entities/#{URI.encode(entity_id)}")
134
+ end
135
+
136
+ def post_values(entity_id, payload)
137
+ payload = payload.map {|k, v| [(k.to_sym rescue k), v]}.to_h.reject{ |k| ![:value, :expressions, :metadata].include?(k) }
138
+ validate_payload payload
139
+ req(logger, @access_token, Net::HTTP::Post, "/entities/#{URI.encode(entity_id)}/values", {}, payload)
140
+ end
141
+
142
+ def delete_values(entity_id, value)
143
+ req(logger, @access_token, Net::HTTP::Delete, "/entities/#{URI.encode(entity_id)}/values/#{URI.encode(value)}")
144
+ end
145
+
146
+ def post_expressions(entity_id, value, payload)
147
+ payload = payload.map {|k, v| [(k.to_sym rescue k), v]}.to_h.reject{ |k| ![:expression].include?(k) }
148
+ validate_payload payload
149
+ req(logger,@access_token, Net::HTTP::Post, "/entities/#{URI.encode(entity_id)}/values/#{URI.encode(value)}/expressions", {}, payload)
150
+ end
151
+
152
+ def delete_expressions(entity_id, value, expression)
153
+ req(logger,@access_token, Net::HTTP::Delete, "/entities/#{URI.encode(entity_id)}/values/#{URI.encode(value)}/expressions/#{URI.encode(expression)}")
154
+ end
155
+
156
+ private
157
+
158
+ def validate_payload(payload)
159
+ key_types = {
160
+ id: String,
161
+ doc: String,
162
+ value: String,
163
+ values: Array,
164
+ lookups: Array,
165
+ expression: String,
166
+ expressions: Array,
167
+ metadata: String,
168
+ }
169
+ payload.each do |k, v|
170
+ raise Error.new("#{k.to_s} in request body must be #{key_types[k].to_s} type") unless key_types[k] == v.class
171
+ end
172
+ end
173
+
105
174
  def __run_actions(session_id, current_request, message, context, i)
106
175
  if i <= 0
107
- raise WitException.new('Max steps reached, stopping.')
176
+ raise Error.new('Max steps reached, stopping.')
108
177
  end
109
178
  json = converse(session_id, message, context)
110
179
  if json['type'].nil?
111
- raise WitException.new('Couldn\'t find type in Wit response')
180
+ raise Error.new('Couldn\'t find type in Wit response')
112
181
  end
113
182
  if current_request != @_sessions[session_id]
114
183
  return context
@@ -124,7 +193,7 @@ class Wit
124
193
  end
125
194
 
126
195
  if json['type'] == 'error'
127
- raise WitException.new('Oops, I don\'t know what to do.')
196
+ raise Error.new('Oops, I don\'t know what to do.')
128
197
  end
129
198
 
130
199
  if json['type'] == 'stop'
@@ -153,7 +222,7 @@ class Wit
153
222
  context = {}
154
223
  end
155
224
  else
156
- raise WitException.new("unknown type: #{json['type']}")
225
+ raise Error.new("unknown type: #{json['type']}")
157
226
  end
158
227
 
159
228
  if current_request != @_sessions[session_id]
@@ -163,66 +232,52 @@ class Wit
163
232
  return __run_actions(session_id, current_request, nil, context, i - 1)
164
233
  end
165
234
 
166
- def run_actions(session_id, message, context={}, max_steps=DEFAULT_MAX_STEPS)
167
- if !@actions
168
- throw_must_have_actions
169
- end
170
- if !context.is_a?(Hash)
171
- raise WitException.new('context should be a Hash')
172
- end
235
+ def req(logger, access_token, meth_class, path, params={}, payload={})
236
+ uri = URI(WIT_API_HOST + path)
237
+ uri.query = URI.encode_www_form(params)
173
238
 
174
- # Figuring out whether we need to reset the last turn.
175
- # Each new call increments an index for the session.
176
- # We only care about the last call to run_actions.
177
- # All the previous ones are discarded (preemptive exit).
178
- current_request = 1
179
- if @_sessions.has_key?(session_id)
180
- current_request = @_sessions[session_id] + 1
181
- end
182
- @_sessions[session_id] = current_request
239
+ logger.debug("#{meth_class} #{uri}")
183
240
 
184
- context = __run_actions(session_id, current_request, message, context, max_steps)
241
+ request = meth_class.new(uri)
242
+ request['authorization'] = 'Bearer ' + access_token
243
+ request['accept'] = 'application/vnd.wit.' + WIT_API_VERSION + '+json'
244
+ request.add_field 'Content-Type', 'application/json'
245
+ request.body = payload.to_json
185
246
 
186
- # Cleaning up once the last call to run_actions finishes.
187
- if current_request == @_sessions[session_id]
188
- @_sessions.delete(session_id)
247
+ Net::HTTP.start(uri.host, uri.port, {:use_ssl => uri.scheme == 'https'}) do |http|
248
+ rsp = http.request(request)
249
+ if rsp.code.to_i != 200
250
+ raise Error.new("HTTP error code=#{rsp.code}")
251
+ end
252
+ json = JSON.parse(rsp.body)
253
+ if json.is_a?(Hash) and json.has_key?('error')
254
+ raise Error.new("Wit responded with an error: #{json['error']}")
255
+ end
256
+ logger.debug("#{meth_class} #{uri} #{json}")
257
+ json
189
258
  end
190
-
191
- return context
192
259
  end
193
260
 
194
- def interactive(context={}, max_steps=DEFAULT_MAX_STEPS)
195
- if !@actions
196
- throw_must_have_actions
261
+ def validate_actions(logger, actions)
262
+ [:send].each do |action|
263
+ if !actions.has_key?(action)
264
+ logger.warn "The #{action} action is missing. #{LEARN_MORE}"
265
+ end
197
266
  end
198
-
199
- session_id = SecureRandom.uuid
200
- while true
201
- print '> '
202
- msg = gets.strip
203
- if msg == ''
204
- next
267
+ actions.each_pair do |k, v|
268
+ if !k.is_a?(Symbol)
269
+ logger.warn "The '#{k}' action name should be a symbol"
205
270
  end
206
-
207
- begin
208
- context = run_actions(session_id, msg, context, max_steps)
209
- rescue WitException => exp
210
- logger.error("error: #{exp.message}")
271
+ if !(v.respond_to?(:call) && v.lambda?)
272
+ logger.warn "The '#{k}' action should be a lambda function"
273
+ end
274
+ if k == :send && v.arity != 2
275
+ logger.warn "The \'send\' action should take 2 arguments: request and response. #{LEARN_MORE}"
276
+ end
277
+ if k != :send && v.arity != 1
278
+ logger.warn "The '#{k}' action should take 1 argument: request. #{LEARN_MORE}"
211
279
  end
212
280
  end
213
- rescue Interrupt => _exp
214
- puts
215
- end
216
-
217
- def throw_if_action_missing(action_name)
218
- if !@actions.has_key?(action_name)
219
- raise WitException.new("unknown action: #{action_name}")
220
- end
281
+ return actions
221
282
  end
222
-
223
- def throw_must_have_actions()
224
- raise WitException.new('You must provide the `actions` parameter to be able to use runActions. ' + LEARN_MORE)
225
- end
226
-
227
- private :__run_actions
228
283
  end
data/wit.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'wit'
3
- s.version = '4.1.0'
3
+ s.version = '5.0.0'
4
4
  s.date = Date.today.to_s
5
5
  s.summary = 'Ruby SDK for Wit.ai'
6
6
  s.description = 'Ruby SDK for Wit.ai'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wit
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.1.0
4
+ version: 5.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - The Wit Team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-08-17 00:00:00.000000000 Z
11
+ date: 2017-07-24 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Ruby SDK for Wit.ai
14
14
  email: help@wit.ai
@@ -16,7 +16,7 @@ executables: []
16
16
  extensions: []
17
17
  extra_rdoc_files: []
18
18
  files:
19
- - ".gitignore"
19
+ - .gitignore
20
20
  - CHANGES.md
21
21
  - Gemfile
22
22
  - LICENSE
@@ -36,17 +36,17 @@ require_paths:
36
36
  - lib
37
37
  required_ruby_version: !ruby/object:Gem::Requirement
38
38
  requirements:
39
- - - ">="
39
+ - - '>='
40
40
  - !ruby/object:Gem::Version
41
41
  version: 1.9.3
42
42
  required_rubygems_version: !ruby/object:Gem::Requirement
43
43
  requirements:
44
- - - ">="
44
+ - - '>='
45
45
  - !ruby/object:Gem::Version
46
46
  version: '0'
47
47
  requirements: []
48
48
  rubyforge_project:
49
- rubygems_version: 2.4.5
49
+ rubygems_version: 2.0.14.1
50
50
  signing_key:
51
51
  specification_version: 4
52
52
  summary: Ruby SDK for Wit.ai