wit 4.1.0 → 5.0.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
  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