xip-luis 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +30 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +100 -0
- data/LICENSE +7 -0
- data/README.md +314 -0
- data/lib/xip-luis.rb +3 -0
- data/lib/xip/nlp/luis.rb +4 -0
- data/lib/xip/nlp/luis/client.rb +62 -0
- data/lib/xip/nlp/luis/result.rb +131 -0
- data/spec/spec_helper.rb +84 -0
- data/xip-luis.gemspec +22 -0
- metadata +96 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 870278eff7d1137878825a9e25be8f83c46b45f3763091e6671427f9df6cf75b
|
4
|
+
data.tar.gz: 340a78e02e9db834799a49e9ec9d196f6bf7cef20f782d74c3291ed5790548a9
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: edf4ba99ed57c1ce36c729436675a24df7041d2e4557b621decd9fce3fa4b5858c31f5785eebe0104ab1c3d8deb6fe4708b4d6df0b0aee0172085fdf88b8e3f9
|
7
|
+
data.tar.gz: 0fadea07ccdc34a113e750668da7823bebfd710635b8ae1c152723a7c9475015dedea9fa5dfe3f50b7b5c31bf80654558b1468c4badc84b0447f4f78c2ff9628
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# Changelog 1.2.1
|
2
|
+
|
3
|
+
## Features
|
4
|
+
|
5
|
+
* Updated gems
|
6
|
+
|
7
|
+
# Changelog 1.2.0
|
8
|
+
|
9
|
+
## Bugfixes
|
10
|
+
|
11
|
+
* `datetimeReference` is not respected by LUIS when specified as a `GET`. Switched to `POST`
|
12
|
+
* Renamed `tz_offset` option to `datetime_reference` to more closely match the LUIS API
|
13
|
+
|
14
|
+
# Changelog 1.1.0
|
15
|
+
|
16
|
+
## Features
|
17
|
+
|
18
|
+
* Added intent threshold option; intents with lower scores than the threshold are not matched.
|
19
|
+
|
20
|
+
# Changelog 1.0.0
|
21
|
+
|
22
|
+
## Bugfixes
|
23
|
+
|
24
|
+
* The endpoint is now properly configurable
|
25
|
+
* Updated gem dependencies
|
26
|
+
* Fixed README to point out correct config
|
27
|
+
|
28
|
+
# Changelog 0.9.0
|
29
|
+
|
30
|
+
* Initial release
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
xip-luis (1.2.1)
|
5
|
+
http (~> 4)
|
6
|
+
xip (~> 2.0.0.beta)
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: https://rubygems.org/
|
10
|
+
specs:
|
11
|
+
activesupport (6.1.0)
|
12
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
13
|
+
i18n (>= 1.6, < 2)
|
14
|
+
minitest (>= 5.1)
|
15
|
+
tzinfo (~> 2.0)
|
16
|
+
zeitwerk (~> 2.3)
|
17
|
+
addressable (2.7.0)
|
18
|
+
public_suffix (>= 2.0.2, < 5.0)
|
19
|
+
concurrent-ruby (1.1.7)
|
20
|
+
connection_pool (2.2.3)
|
21
|
+
diff-lcs (1.4.4)
|
22
|
+
domain_name (0.5.20190701)
|
23
|
+
unf (>= 0.0.5, < 1.0.0)
|
24
|
+
ffi (1.14.2)
|
25
|
+
ffi-compiler (1.0.1)
|
26
|
+
ffi (>= 1.0.0)
|
27
|
+
rake
|
28
|
+
http (4.4.1)
|
29
|
+
addressable (~> 2.3)
|
30
|
+
http-cookie (~> 1.0)
|
31
|
+
http-form_data (~> 2.2)
|
32
|
+
http-parser (~> 1.2.0)
|
33
|
+
http-cookie (1.0.3)
|
34
|
+
domain_name (~> 0.5)
|
35
|
+
http-form_data (2.3.0)
|
36
|
+
http-parser (1.2.2)
|
37
|
+
ffi-compiler
|
38
|
+
i18n (1.8.5)
|
39
|
+
concurrent-ruby (~> 1.0)
|
40
|
+
minitest (5.14.2)
|
41
|
+
multi_json (1.15.0)
|
42
|
+
mustermann (1.1.1)
|
43
|
+
ruby2_keywords (~> 0.0.1)
|
44
|
+
nio4r (2.5.4)
|
45
|
+
public_suffix (4.0.6)
|
46
|
+
puma (4.3.7)
|
47
|
+
nio4r (~> 2.0)
|
48
|
+
rack (2.2.3)
|
49
|
+
rack-protection (2.1.0)
|
50
|
+
rack
|
51
|
+
rake (13.0.3)
|
52
|
+
redis (4.2.5)
|
53
|
+
rspec (3.9.0)
|
54
|
+
rspec-core (~> 3.9.0)
|
55
|
+
rspec-expectations (~> 3.9.0)
|
56
|
+
rspec-mocks (~> 3.9.0)
|
57
|
+
rspec-core (3.9.2)
|
58
|
+
rspec-support (~> 3.9.3)
|
59
|
+
rspec-expectations (3.9.2)
|
60
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
61
|
+
rspec-support (~> 3.9.0)
|
62
|
+
rspec-mocks (3.9.1)
|
63
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
64
|
+
rspec-support (~> 3.9.0)
|
65
|
+
rspec-support (3.9.3)
|
66
|
+
ruby2_keywords (0.0.2)
|
67
|
+
sidekiq (6.1.2)
|
68
|
+
connection_pool (>= 2.2.2)
|
69
|
+
rack (~> 2.0)
|
70
|
+
redis (>= 4.2.0)
|
71
|
+
sinatra (2.1.0)
|
72
|
+
mustermann (~> 1.0)
|
73
|
+
rack (~> 2.2)
|
74
|
+
rack-protection (= 2.1.0)
|
75
|
+
tilt (~> 2.0)
|
76
|
+
thor (1.0.1)
|
77
|
+
tilt (2.0.10)
|
78
|
+
tzinfo (2.0.4)
|
79
|
+
concurrent-ruby (~> 1.0)
|
80
|
+
unf (0.1.4)
|
81
|
+
unf_ext
|
82
|
+
unf_ext (0.0.7.7)
|
83
|
+
xip (2.0.0.beta2)
|
84
|
+
activesupport (~> 6.0)
|
85
|
+
multi_json (~> 1.12)
|
86
|
+
puma (>= 4.2, < 5.0)
|
87
|
+
sidekiq (~> 6.0)
|
88
|
+
sinatra (~> 2.0)
|
89
|
+
thor (~> 1.0)
|
90
|
+
zeitwerk (2.4.2)
|
91
|
+
|
92
|
+
PLATFORMS
|
93
|
+
ruby
|
94
|
+
|
95
|
+
DEPENDENCIES
|
96
|
+
rspec (~> 3)
|
97
|
+
xip-luis!
|
98
|
+
|
99
|
+
BUNDLED WITH
|
100
|
+
2.1.4
|
data/LICENSE
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
Copyright (c) 2017-2020 Mauricio Gomes
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
4
|
+
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
6
|
+
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,314 @@
|
|
1
|
+
# LUIS NLP Xip Kit Component
|
2
|
+
|
3
|
+
This integration implements the [Microsoft LUIS](https://luis.ai) Language Understanding service. It utilizes the built-in NLP features part of Xip 2.x. If you are still using Xip 1.x, you will first need to upgrade to Xip 2.x before you can use this integration.
|
4
|
+
|
5
|
+
## Configuration
|
6
|
+
|
7
|
+
For instructions on how to configure your Azure account signup for LUIS, please reference their docs. You won't have to set this anywhere, but this gem does utilize the latest `v3` LUIS API version.
|
8
|
+
|
9
|
+
Once your account is setup, these are the configuration settings you will need to add to you `services.yml` file:
|
10
|
+
|
11
|
+
```yaml
|
12
|
+
default: &default
|
13
|
+
nlp_integration: luis
|
14
|
+
luis:
|
15
|
+
endpoint: westus.api.cognitive.microsoft.com
|
16
|
+
app_id: 9434fbd8-420b-6d75-8a6f-b6c9a0ac5ec0
|
17
|
+
subscription_key: 1b69a4b9db669805b4fcba5f1f2f87bb
|
18
|
+
tz_offset: 0
|
19
|
+
intent_threshold: 0.2
|
20
|
+
|
21
|
+
production:
|
22
|
+
<<: *default
|
23
|
+
development:
|
24
|
+
<<: *default
|
25
|
+
test:
|
26
|
+
<<: *default
|
27
|
+
```
|
28
|
+
|
29
|
+
Xip will automatically use your `staging` LUIS slot in development and staging environments and will use the `production` slot for your production Xip environment.
|
30
|
+
|
31
|
+
That's it! Xip will now automatically use LUIS for intent detection and entity extraction automatically via `handle_response` and `get_match`.
|
32
|
+
|
33
|
+
## Intents
|
34
|
+
|
35
|
+
We recommend you name your intents using snake case (`snake_case`). This is because this integration will automatically convert your intent names to Ruby symbols.
|
36
|
+
|
37
|
+
So for example, if you have a `handle_response` defined like this:
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
handle_response(
|
41
|
+
'Maybe' => proc { step_to state: :say_maybe },
|
42
|
+
:yes => proc { step_to state: :say_yes },
|
43
|
+
:no => proc { step_to state: :say_no }
|
44
|
+
)
|
45
|
+
```
|
46
|
+
|
47
|
+
If your user responds with a variation of the string `maybe`, then they will be taken to the state `say_maybe`.
|
48
|
+
|
49
|
+
Otherwise, the intent named `yes` and the intent named `no` will attempt to be matched. So if you had named your intent `YES` for example, you'd have to use `:YES` here which doesn't match Ruby syntax conventions.
|
50
|
+
|
51
|
+
For more info about how intents are matched, please see the [Xip Kit NLP documentation](https://docs.xipkit.com/nlp).
|
52
|
+
|
53
|
+
### intent_threshold
|
54
|
+
|
55
|
+
This is the real number that respresents the minimum threshold required for an intent to match. So if for example your `intent_threshold` is set to `0.2`, if the top intent scores `0.09`, it will not be returned as a match.
|
56
|
+
|
57
|
+
## Entities
|
58
|
+
|
59
|
+
The entity types listed below are named using their corresponding Xip type. The equivalent type used by Microsoft LUIS is also listed. For each code sample, the sample query is first provided followed by the array of entities extracted from the queries (for the given type).
|
60
|
+
|
61
|
+
It's possible, and even likely, that a query matches more than one entity type. For example, a `currency` type will also match a `number` type. For more info about how to utilize these types, please see the [Xip Kit NLP documentation](https://docs.xipkit.com/nlp).
|
62
|
+
|
63
|
+
### number
|
64
|
+
|
65
|
+
LUIS prebuilt entity: `number`
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
"I think it was something like 63 or maybe 764"
|
69
|
+
|
70
|
+
[
|
71
|
+
63,
|
72
|
+
764
|
73
|
+
]
|
74
|
+
```
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
"It was almost 15k"
|
78
|
+
|
79
|
+
[
|
80
|
+
15000
|
81
|
+
]
|
82
|
+
```
|
83
|
+
|
84
|
+
For more info about these values, please reference the [number entity LUIS documentation](https://docs.microsoft.com/en-us/azure/cognitive-services/luis/luis-reference-prebuilt-number?tabs=V3).
|
85
|
+
|
86
|
+
### currency
|
87
|
+
|
88
|
+
LUIS prebuilt entity: `money`
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
"send me $87 or 48 cents"
|
92
|
+
|
93
|
+
[
|
94
|
+
{ 'number' => 87, 'units' => 'Dollar' },
|
95
|
+
{ 'number' => 48, 'units': 'Cent' }
|
96
|
+
]
|
97
|
+
```
|
98
|
+
|
99
|
+
For more info about these values, please reference the [money entity LUIS documentation](https://docs.microsoft.com/en-us/azure/cognitive-services/luis/luis-reference-prebuilt-currency?tabs=V3).
|
100
|
+
|
101
|
+
### email
|
102
|
+
|
103
|
+
LUIS prebuilt entity: `email`
|
104
|
+
|
105
|
+
```ruby
|
106
|
+
"you can contact me at john@email.none"
|
107
|
+
|
108
|
+
[
|
109
|
+
"john@email.none"
|
110
|
+
]
|
111
|
+
```
|
112
|
+
|
113
|
+
For more info about these values, please reference the [email entity LUIS documentation](https://docs.microsoft.com/en-us/azure/cognitive-services/luis/luis-reference-prebuilt-email?tabs=V3).
|
114
|
+
|
115
|
+
### phone
|
116
|
+
|
117
|
+
LUIS prebuilt entity: `phonenumber`
|
118
|
+
|
119
|
+
Note: LUIS does not parse nor attempts to clean up phone number.
|
120
|
+
|
121
|
+
```ruby
|
122
|
+
"You can reach me at 313-555-1212"
|
123
|
+
|
124
|
+
[
|
125
|
+
"313-555-1212"
|
126
|
+
]
|
127
|
+
```
|
128
|
+
|
129
|
+
For more info about these values, please reference the [phonenumber entity LUIS documentation](https://docs.microsoft.com/en-us/azure/cognitive-services/luis/luis-reference-prebuilt-phonenumber?tabs=V3).
|
130
|
+
|
131
|
+
### percentage
|
132
|
+
|
133
|
+
LUIS prebuilt entity: `percentage`
|
134
|
+
|
135
|
+
```ruby
|
136
|
+
"The stock is up 8.9% today"
|
137
|
+
|
138
|
+
[
|
139
|
+
8.9
|
140
|
+
]
|
141
|
+
```
|
142
|
+
|
143
|
+
For more info about these values, please reference the [percentage entity LUIS documentation](https://docs.microsoft.com/en-us/azure/cognitive-services/luis/luis-reference-prebuilt-percentage?tabs=V3).
|
144
|
+
|
145
|
+
### age
|
146
|
+
|
147
|
+
LUIS prebuilt entity: `age`
|
148
|
+
|
149
|
+
```ruby
|
150
|
+
"81 years old"
|
151
|
+
|
152
|
+
[
|
153
|
+
{ 'number' => 81, 'units' => 'Year' }
|
154
|
+
]
|
155
|
+
```
|
156
|
+
|
157
|
+
For more info about these values, please reference the [age entity LUIS documentation](https://docs.microsoft.com/en-us/azure/cognitive-services/luis/luis-reference-prebuilt-age?tabs=V3).
|
158
|
+
|
159
|
+
### url
|
160
|
+
|
161
|
+
LUIS prebuilt entity: `url`
|
162
|
+
|
163
|
+
```ruby
|
164
|
+
"please visit google.com or https://google.com"
|
165
|
+
|
166
|
+
[
|
167
|
+
"google.com",
|
168
|
+
"https://google.com"
|
169
|
+
]
|
170
|
+
|
171
|
+
```
|
172
|
+
|
173
|
+
For more info about these values, please reference the [url entity LUIS documentation](https://docs.microsoft.com/en-us/azure/cognitive-services/luis/luis-reference-prebuilt-url?tabs=V3).
|
174
|
+
|
175
|
+
### ordinal
|
176
|
+
|
177
|
+
LUIS prebuilt entity: `ordinalV2`
|
178
|
+
|
179
|
+
```ruby
|
180
|
+
"they finished 2nd and 5th"
|
181
|
+
|
182
|
+
[
|
183
|
+
{ 'offset' => 2, 'relativeTo' => 'start' },
|
184
|
+
{ 'offset' => 5, 'relativeTo' => 'start' }
|
185
|
+
]
|
186
|
+
```
|
187
|
+
|
188
|
+
```ruby
|
189
|
+
"she finished last"
|
190
|
+
|
191
|
+
[
|
192
|
+
{ 'offset' => 0, 'relativeTo' => 'end' }
|
193
|
+
]
|
194
|
+
```
|
195
|
+
|
196
|
+
For more info about these values, please reference the [ordinalV2 entity LUIS documentation](https://docs.microsoft.com/en-us/azure/cognitive-services/luis/luis-reference-prebuilt-ordinal-v2?tabs=V3).
|
197
|
+
|
198
|
+
### geo
|
199
|
+
|
200
|
+
LUIS prebuilt entity: `geographyV2`
|
201
|
+
|
202
|
+
```ruby
|
203
|
+
"She moved to paris, france"
|
204
|
+
|
205
|
+
[
|
206
|
+
{ 'value' => 'paris', 'type' => 'city' },
|
207
|
+
{ 'value' => 'france', 'type' => 'countryRegion' }
|
208
|
+
]
|
209
|
+
```
|
210
|
+
|
211
|
+
For more info about these values, please reference the [geographyV2 entity LUIS documentation](https://docs.microsoft.com/en-us/azure/cognitive-services/luis/luis-reference-prebuilt-geographyv2?tabs=V3).
|
212
|
+
|
213
|
+
### dimension
|
214
|
+
|
215
|
+
LUIS prebuilt entity: `dimension`
|
216
|
+
|
217
|
+
```ruby
|
218
|
+
"it's about 4 inches wide"
|
219
|
+
|
220
|
+
[
|
221
|
+
{ "number": 4, "units": "Inch" }
|
222
|
+
]
|
223
|
+
```
|
224
|
+
|
225
|
+
For more info about these values, please reference the [dimension entity LUIS documentation](https://docs.microsoft.com/en-us/azure/cognitive-services/luis/luis-reference-prebuilt-dimension?tabs=V3).
|
226
|
+
|
227
|
+
### temp
|
228
|
+
|
229
|
+
LUIS prebuilt entity: `temperature`
|
230
|
+
|
231
|
+
```ruby
|
232
|
+
"it feels like 98 degrees"
|
233
|
+
|
234
|
+
[
|
235
|
+
{ 'number' => 98, 'units' => 'Degree' }
|
236
|
+
]
|
237
|
+
```
|
238
|
+
|
239
|
+
For more info about these values, please reference the [temperature entity LUIS documentation](https://docs.microsoft.com/en-us/azure/cognitive-services/luis/luis-reference-prebuilt-temperature?tabs=V3).
|
240
|
+
|
241
|
+
### datetime
|
242
|
+
|
243
|
+
LUIS prebuilt entity: `datetimeV2`
|
244
|
+
|
245
|
+
This one is the most complicated one to work with. The values are nested pretty deeply. This integration exposes the values at such a high level because there is a chance that LUIS will return results for more than one date type. For example, below we have just one result of type `date`, but LUIS could return more than one object of subtype `daterange`, `time`, `timerange`, etc. See the docs for more info about these subtypes.
|
246
|
+
|
247
|
+
```ruby
|
248
|
+
"How about Mar 12?"
|
249
|
+
|
250
|
+
[
|
251
|
+
{
|
252
|
+
"type": "date",
|
253
|
+
"values": [
|
254
|
+
{
|
255
|
+
"timex": "XXXX-03-12",
|
256
|
+
"resolution": [
|
257
|
+
{
|
258
|
+
"value": "2019-03-12"
|
259
|
+
},
|
260
|
+
{
|
261
|
+
"value": "2020-03-12"
|
262
|
+
}
|
263
|
+
]
|
264
|
+
}
|
265
|
+
]
|
266
|
+
}
|
267
|
+
]
|
268
|
+
```
|
269
|
+
|
270
|
+
For more info about these values, please reference the [datetimeV2 entity LUIS documentation](https://docs.microsoft.com/en-us/azure/cognitive-services/luis/luis-reference-prebuilt-datetimev2?tabs=1-1%2C2-1%2C3-1%2C4-1%2C5-1%2C6-1#types-of-datetimev2).
|
271
|
+
|
272
|
+
### duration
|
273
|
+
|
274
|
+
LUIS prebuilt domain entity: `Calendar.Duration`
|
275
|
+
|
276
|
+
```ruby
|
277
|
+
"it will be between 15 minutes and 3 hours"
|
278
|
+
|
279
|
+
[
|
280
|
+
"15 minutes",
|
281
|
+
"3 hours"
|
282
|
+
]
|
283
|
+
```
|
284
|
+
|
285
|
+
_Additional docs for this prebuilt domain entitiy is not available_
|
286
|
+
|
287
|
+
### key_phrase
|
288
|
+
|
289
|
+
LUIS prebuilt entity: `keyPhrase`
|
290
|
+
|
291
|
+
```ruby
|
292
|
+
"I need to find the instructional materials for the course"
|
293
|
+
|
294
|
+
[
|
295
|
+
"instructional materials",
|
296
|
+
"course"
|
297
|
+
]
|
298
|
+
```
|
299
|
+
|
300
|
+
For more info about these values, please reference the [keyPhrase entity LUIS documentation](https://docs.microsoft.com/en-us/azure/cognitive-services/luis/luis-reference-prebuilt-keyphrase?tabs=V3).
|
301
|
+
|
302
|
+
### name
|
303
|
+
|
304
|
+
LUIS prebuilt entity: `personName`
|
305
|
+
|
306
|
+
```ruby
|
307
|
+
"Little Cindy-Lou Who who was not more than two"
|
308
|
+
|
309
|
+
[
|
310
|
+
"Little Cindy-Lou"
|
311
|
+
]
|
312
|
+
```
|
313
|
+
|
314
|
+
For more info about these values, please reference the [personName entity LUIS documentation](https://docs.microsoft.com/en-us/azure/cognitive-services/luis/luis-reference-prebuilt-person?tabs=V3).
|
data/lib/xip-luis.rb
ADDED
data/lib/xip/nlp/luis.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Xip
|
4
|
+
module Nlp
|
5
|
+
module Luis
|
6
|
+
class Client < Xip::Nlp::Client
|
7
|
+
|
8
|
+
def initialize(subscription_key: nil, app_id: nil, endpoint: nil, datetime_ref: nil)
|
9
|
+
begin
|
10
|
+
@subscription_key = subscription_key || Xip.config.luis.subscription_key
|
11
|
+
@app_id = app_id || Xip.config.luis.app_id
|
12
|
+
@endpoint = endpoint || Xip.config.luis.endpoint
|
13
|
+
@datetime_ref = datetime_ref || Xip.config.luis.datetime_reference
|
14
|
+
@slot = Xip.env.development? ? 'staging' : 'production'
|
15
|
+
rescue NoMethodError
|
16
|
+
raise(
|
17
|
+
Xip::Errors::ConfigurationError,
|
18
|
+
'A `luis` configuration key must be specified directly or in `services.yml`'
|
19
|
+
)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def endpoint
|
24
|
+
"https://#{@endpoint}/luis/prediction/v3.0/apps/#{@app_id}/slots/#{@slot}/predict"
|
25
|
+
end
|
26
|
+
|
27
|
+
def client
|
28
|
+
@client ||= begin
|
29
|
+
headers = {
|
30
|
+
'Content-Type' => 'application/json'
|
31
|
+
}
|
32
|
+
HTTP.timeout(connect: 15, read: 60).headers(headers)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def understand(query:)
|
37
|
+
params = {
|
38
|
+
'subscription-key' => @subscription_key
|
39
|
+
}
|
40
|
+
|
41
|
+
body = MultiJson.dump({
|
42
|
+
'query' => query,
|
43
|
+
'options' => {
|
44
|
+
'datetimeReference' => @datetime_ref,
|
45
|
+
}
|
46
|
+
})
|
47
|
+
|
48
|
+
Xip::Logger.l(
|
49
|
+
topic: :nlp,
|
50
|
+
message: 'Performing NLP lookup via Microsoft LUIS'
|
51
|
+
)
|
52
|
+
result = client.post(endpoint, params: params, body: body)
|
53
|
+
Result.new(result: result)
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
ENTITY_TYPES = %i(number currency email percentage phone age
|
62
|
+
url ordinal geo dimension temp datetime duration)
|
@@ -0,0 +1,131 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Xip
|
4
|
+
module Nlp
|
5
|
+
module Luis
|
6
|
+
class Result < Xip::Nlp::Result
|
7
|
+
|
8
|
+
ENTITY_MAP = {
|
9
|
+
'money' => :currency, 'number' => :number, 'email' => :email,
|
10
|
+
'percentage' => :percentage, 'Calendar.Duration' => :duration,
|
11
|
+
'geographyV2' => :geo, 'age' => :age, 'phonenumber' => :phone,
|
12
|
+
'ordinalV2' => :ordinal, 'url' => :url, 'dimension' => :dimension,
|
13
|
+
'temperature' => :temp, 'keyPhrase' => :key_phrase, 'name' => :name,
|
14
|
+
'datetimeV2' => :datetime
|
15
|
+
}
|
16
|
+
|
17
|
+
def initialize(result:)
|
18
|
+
@result = result
|
19
|
+
if result.status.success?
|
20
|
+
Xip::Logger.l(
|
21
|
+
topic: :nlp,
|
22
|
+
message: 'NLP lookup successful'
|
23
|
+
)
|
24
|
+
parsed_result
|
25
|
+
else
|
26
|
+
Xip::Logger.l(
|
27
|
+
topic: :nlp,
|
28
|
+
message: "NLP lookup FAILED: (#{result.status.code}) #{result.body.to_s}"
|
29
|
+
)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Sample JSON result:
|
34
|
+
# {
|
35
|
+
# "query": "I make between $5400 and $9600 per month",
|
36
|
+
# "prediction": {
|
37
|
+
# "topIntent": "None",
|
38
|
+
# "intents": {
|
39
|
+
# "None": {
|
40
|
+
# "score": 0.5345857
|
41
|
+
# }
|
42
|
+
# },
|
43
|
+
# "entities": {
|
44
|
+
# "money": [
|
45
|
+
# {
|
46
|
+
# "number": 5400,
|
47
|
+
# "units": "Dollar"
|
48
|
+
# },
|
49
|
+
# {
|
50
|
+
# "number": 9600,
|
51
|
+
# "units": "Dollar"
|
52
|
+
# }
|
53
|
+
# ],
|
54
|
+
# "number": [
|
55
|
+
# 5400,
|
56
|
+
# 9600
|
57
|
+
# ]
|
58
|
+
# },
|
59
|
+
# "sentiment": {
|
60
|
+
# "label": "positive",
|
61
|
+
# "score": 0.7805586
|
62
|
+
# }
|
63
|
+
# }
|
64
|
+
# }
|
65
|
+
def parsed_result
|
66
|
+
@parsed_result ||= MultiJson.load(result.body.to_s)
|
67
|
+
end
|
68
|
+
|
69
|
+
def intent
|
70
|
+
top_intent&.to_sym
|
71
|
+
end
|
72
|
+
|
73
|
+
def intent_score
|
74
|
+
parsed_result&.dig('prediction', 'intents', top_intent, 'score')
|
75
|
+
end
|
76
|
+
|
77
|
+
def raw_entities
|
78
|
+
parsed_result&.dig('prediction', 'entities')
|
79
|
+
end
|
80
|
+
|
81
|
+
def entities
|
82
|
+
return {} if raw_entities.blank?
|
83
|
+
_entities = {}
|
84
|
+
|
85
|
+
raw_entities.each do |type, values|
|
86
|
+
if ENTITY_MAP[type]
|
87
|
+
_entities[ENTITY_MAP[type]] = values
|
88
|
+
else
|
89
|
+
# A custom entity
|
90
|
+
_entities[type.to_sym] = values
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
_entities
|
95
|
+
end
|
96
|
+
|
97
|
+
def sentiment_score
|
98
|
+
parsed_result&.dig('prediction', 'sentiment', 'score')
|
99
|
+
end
|
100
|
+
|
101
|
+
def sentiment
|
102
|
+
parsed_result&.dig('prediction', 'sentiment', 'label')&.to_sym
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
def top_intent
|
108
|
+
@top_intent ||= begin
|
109
|
+
matched_intent = parsed_result&.dig('prediction', 'topIntent')
|
110
|
+
_intent_score = parsed_result&.dig('prediction', 'intents', matched_intent, 'score')
|
111
|
+
|
112
|
+
if Xip.config.luis.intent_threshold.is_a?(Numeric)
|
113
|
+
if _intent_score > Xip.config.luis.intent_threshold
|
114
|
+
matched_intent
|
115
|
+
else
|
116
|
+
Xip::Logger.l(
|
117
|
+
topic: :nlp,
|
118
|
+
message: "Ignoring intent match. Does not meet threshold (#{Xip.config.luis.intent_threshold})"
|
119
|
+
)
|
120
|
+
'None' # can't be nil or this doesn't get memoized
|
121
|
+
end
|
122
|
+
else
|
123
|
+
matched_intent
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
+
|
4
|
+
require 'rspec'
|
5
|
+
require 'xip-luis'
|
6
|
+
|
7
|
+
RSpec.configure do |config|
|
8
|
+
config.expect_with :rspec do |expectations|
|
9
|
+
# This option will default to `true` in RSpec 4. It makes the `description`
|
10
|
+
# and `failure_message` of custom matchers include text for helper methods
|
11
|
+
# defined using `chain`, e.g.:
|
12
|
+
# be_bigger_than(2).and_smaller_than(4).description
|
13
|
+
# # => "be bigger than 2 and smaller than 4"
|
14
|
+
# ...rather than:
|
15
|
+
# # => "be bigger than 2"
|
16
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
17
|
+
end
|
18
|
+
|
19
|
+
# rspec-mocks config goes here. You can use an alternate test double
|
20
|
+
# library (such as bogus or mocha) by changing the `mock_with` option here.
|
21
|
+
config.mock_with :rspec do |mocks|
|
22
|
+
# Prevents you from mocking or stubbing a method that does not exist on
|
23
|
+
# a real object. This is generally recommended, and will default to
|
24
|
+
# `true` in RSpec 4.
|
25
|
+
mocks.verify_partial_doubles = true
|
26
|
+
end
|
27
|
+
|
28
|
+
# This option will default to `:apply_to_host_groups` in RSpec 4 (and will
|
29
|
+
# have no way to turn it off -- the option exists only for backwards
|
30
|
+
# compatibility in RSpec 3). It causes shared context metadata to be
|
31
|
+
# inherited by the metadata hash of host groups and examples, rather than
|
32
|
+
# triggering implicit auto-inclusion in groups with matching metadata.
|
33
|
+
config.shared_context_metadata_behavior = :apply_to_host_groups
|
34
|
+
|
35
|
+
# The settings below are suggested to provide a good initial experience
|
36
|
+
# with RSpec, but feel free to customize to your heart's content.
|
37
|
+
=begin
|
38
|
+
# This allows you to limit a spec run to individual examples or groups
|
39
|
+
# you care about by tagging them with `:focus` metadata. When nothing
|
40
|
+
# is tagged with `:focus`, all examples get run. RSpec also provides
|
41
|
+
# aliases for `it`, `describe`, and `context` that include `:focus`
|
42
|
+
# metadata: `fit`, `fdescribe` and `fcontext`, respectively.
|
43
|
+
config.filter_run_when_matching :focus
|
44
|
+
|
45
|
+
# Allows RSpec to persist some state between runs in order to support
|
46
|
+
# the `--only-failures` and `--next-failure` CLI options. We recommend
|
47
|
+
# you configure your source control system to ignore this file.
|
48
|
+
config.example_status_persistence_file_path = "spec/examples.txt"
|
49
|
+
|
50
|
+
# Limits the available syntax to the non-monkey patched syntax that is
|
51
|
+
# recommended. For more details, see:
|
52
|
+
# - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
|
53
|
+
# - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
|
54
|
+
# - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
|
55
|
+
config.disable_monkey_patching!
|
56
|
+
|
57
|
+
# Many RSpec users commonly either run the entire suite or an individual
|
58
|
+
# file, and it's useful to allow more verbose output when running an
|
59
|
+
# individual spec file.
|
60
|
+
if config.files_to_run.one?
|
61
|
+
# Use the documentation formatter for detailed output,
|
62
|
+
# unless a formatter has already been configured
|
63
|
+
# (e.g. via a command-line flag).
|
64
|
+
config.default_formatter = 'doc'
|
65
|
+
end
|
66
|
+
|
67
|
+
# Print the 10 slowest examples and example groups at the
|
68
|
+
# end of the spec run, to help surface which specs are running
|
69
|
+
# particularly slow.
|
70
|
+
config.profile_examples = 10
|
71
|
+
|
72
|
+
# Run specs in random order to surface order dependencies. If you find an
|
73
|
+
# order dependency and want to debug it, you can fix the order by providing
|
74
|
+
# the seed, which is printed after each run.
|
75
|
+
# --seed 1234
|
76
|
+
config.order = :random
|
77
|
+
|
78
|
+
# Seed global randomization in this process using the `--seed` CLI option.
|
79
|
+
# Setting this allows you to use `--seed` to deterministically reproduce
|
80
|
+
# test failures related to randomization by passing the same `--seed` value
|
81
|
+
# as the one that triggered the failure.
|
82
|
+
Kernel.srand config.seed
|
83
|
+
=end
|
84
|
+
end
|
data/xip-luis.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
$LOAD_PATH.push File.expand_path('../lib', __FILE__)
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = 'xip-luis'
|
5
|
+
s.version = '1.2.1'
|
6
|
+
s.summary = "LUIS NLP Xip Kit Component"
|
7
|
+
s.description = "Built-in NLP for Xip Kit via Microsoft's Language Understanding (LUIS)."
|
8
|
+
s.authors = ["Mauricio Gomes"]
|
9
|
+
s.email = 'mauricio@edge14.com'
|
10
|
+
s.homepage = 'http://github.com/xipkit/xip-luis'
|
11
|
+
s.license = 'MIT'
|
12
|
+
|
13
|
+
s.add_dependency 'xip', '~> 2.0.0.beta'
|
14
|
+
s.add_dependency 'http', '~> 4'
|
15
|
+
|
16
|
+
s.add_development_dependency "rspec", "~> 3"
|
17
|
+
|
18
|
+
s.files = `git ls-files`.split("\n")
|
19
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
20
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
21
|
+
s.require_paths = ['lib']
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: xip-luis
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.2.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mauricio Gomes
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-12-23 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: xip
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 2.0.0.beta
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 2.0.0.beta
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: http
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '4'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '4'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3'
|
55
|
+
description: Built-in NLP for Xip Kit via Microsoft's Language Understanding (LUIS).
|
56
|
+
email: mauricio@edge14.com
|
57
|
+
executables: []
|
58
|
+
extensions: []
|
59
|
+
extra_rdoc_files: []
|
60
|
+
files:
|
61
|
+
- CHANGELOG.md
|
62
|
+
- Gemfile
|
63
|
+
- Gemfile.lock
|
64
|
+
- LICENSE
|
65
|
+
- README.md
|
66
|
+
- lib/xip-luis.rb
|
67
|
+
- lib/xip/nlp/luis.rb
|
68
|
+
- lib/xip/nlp/luis/client.rb
|
69
|
+
- lib/xip/nlp/luis/result.rb
|
70
|
+
- spec/spec_helper.rb
|
71
|
+
- xip-luis.gemspec
|
72
|
+
homepage: http://github.com/xipkit/xip-luis
|
73
|
+
licenses:
|
74
|
+
- MIT
|
75
|
+
metadata: {}
|
76
|
+
post_install_message:
|
77
|
+
rdoc_options: []
|
78
|
+
require_paths:
|
79
|
+
- lib
|
80
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '0'
|
85
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
requirements: []
|
91
|
+
rubygems_version: 3.1.2
|
92
|
+
signing_key:
|
93
|
+
specification_version: 4
|
94
|
+
summary: LUIS NLP Xip Kit Component
|
95
|
+
test_files:
|
96
|
+
- spec/spec_helper.rb
|