1 | # **craft ai** isomorphic javascript client #
|
2 |
|
3 | [![Version](https://img.shields.io/npm/v/craft-ai.svg?style=flat-square)](https://npmjs.org/package/craft-ai) [![Build](https://img.shields.io/travis/craft-ai/craft-ai-client-js/master.svg?style=flat-square)](https://travis-ci.org/craft-ai/craft-ai-client-js) [![License](https://img.shields.io/badge/license-BSD--3--Clause-42358A.svg?style=flat-square)](LICENSE) [![Dependencies](https://img.shields.io/david/craft-ai/craft-ai-client-js.svg?style=flat-square)](https://david-dm.org/craft-ai/craft-ai-client-js) [![Dev Dependencies](https://img.shields.io/david/dev/craft-ai/craft-ai-client-js.svg?style=flat-square)](https://david-dm.org/craft-ai/craft-ai-client-js#info=devDependencies)
|
4 |
|
5 | [**craft ai**'s Explainable AI API](http://craft.ai) enables product & operational teams to quickly deploy and run explainable AIs. craft ai decodes your data streams to deliver self-learning services.
|
6 |
|
7 | ## Get Started!
|
8 |
|
9 | ### 0 - Signup
|
10 |
|
11 | If you're reading this you are probably already registered with **craft ai**, if not, head to [`https://beta.craft.ai/signup`](https://beta.craft.ai/signup).
|
12 |
|
13 | ### 1 - Create a project
|
14 |
|
15 | Once your account is setup, let's create your first **project**! Go in the 'Projects' tab in the **craft ai** control center at [`https://beta.craft.ai/inspector`](https://beta.craft.ai/inspector), and press **Create a project**.
|
16 |
|
17 | Once it's done, you can click on your newly created project to retrieve its tokens. There are two types of tokens: **read** and **write**. You'll need the **write** token to create, update and delete your agent.
|
18 |
|
19 | ### 2 - Setup
|
20 |
|
21 | #### Install ####
|
22 |
|
23 | ##### [Node.js](https://nodejs.org/en/) / [Webpack](http://webpack.github.io) / [Browserify](http://browserify.org) #####
|
24 |
|
25 | Let's first install the package from npm.
|
26 |
|
27 | ```sh
|
28 | npm install craft-ai --save
|
29 | ```
|
30 | Then import it in your code
|
31 |
|
32 | ```js
|
33 | const craftai = require('craft-ai').createClient;
|
34 | ```
|
35 |
|
36 | or using [es2015](https://babeljs.io/docs/learn-es2015/) syntax
|
37 |
|
38 | ```js
|
39 | import craftai from 'craft-ai';
|
40 | ```
|
41 |
|
42 | ##### Plain Old Javascript #####
|
43 |
|
44 | Thanks to [npmcdn](https://npmcdn.com), you can include the pre-generated bundle in your html file, for the latest version use
|
45 |
|
46 | ```html
|
47 | <script type="text/javascript" src="https://npmcdn.com/craft-ai/dist/craft-ai.min.js"></script>
|
48 | ```
|
49 |
|
50 | to include a specific version specify it in the url like
|
51 |
|
52 | ```html
|
53 | <script type="text/javascript" src="https://npmcdn.com/craft-ai@0.1.13/dist/craft-ai.min.js"></script>
|
54 | ```
|
55 |
|
56 | #### Initialize ####
|
57 |
|
58 | ```js
|
59 | // The token you retrieved for a given project
|
60 | const client = craftai('{token}');
|
61 | ```
|
62 |
|
63 | ### 3 - Create an agent
|
64 |
|
65 | **craft ai** is based on the concept of **agents**. In most use cases, one agent is created per user or per device.
|
66 |
|
67 | An agent is an independent module that stores the history of the **context** of its user or device's context, and learns which **decision** to take based on the evolution of this context in the form of a **decision tree**.
|
68 |
|
69 | In this example, we will create an agent that learns the **decision model** of a light bulb based on the time of the day and the number of people in the room. In practice, it means the agent's context have 4 properties:
|
70 |
|
71 | - `peopleCount` which is a `continuous` property,
|
72 | - `timeOfDay` which is a `time_of_day` property,
|
73 | - `timezone`, a property of type `timezone` needed to generate proper values for `timeOfDay` (cf. the [context properties type section](#context-properties-types) for further information),
|
74 | - and finally `lightbulbState` which is an `enum` property that is also the output.
|
75 |
|
76 | > :information_source: `timeOfDay` is auto-generated, you will find more information below.
|
77 |
|
78 | ```js
|
79 | const AGENT_ID = 'my_first_agent';
|
80 |
|
81 | client.createAgent(
|
82 | {
|
83 | context: {
|
84 | peopleCount: {
|
85 | type: 'continuous'
|
86 | },
|
87 | timeOfDay: {
|
88 | type: 'time_of_day'
|
89 | },
|
90 | timezone: {
|
91 | type: 'timezone'
|
92 | },
|
93 | lightbulbState: {
|
94 | type: 'enum'
|
95 | }
|
96 | },
|
97 | output: [ 'lightbulbState' ]
|
98 | },
|
99 | AGENT_ID
|
100 | )
|
101 | .then(function(agent) {
|
102 | console.log('Agent ' + agent.id + ' successfully created!');
|
103 | })
|
104 | .catch(function(error) {
|
105 | console.error('Error!', error);
|
106 | });
|
107 | ```
|
108 |
|
109 | Pretty straightforward to test! Open [`https://beta.craft.ai/inspector`](https://beta.craft.ai/inspector), select you project and your agent is now listed.
|
110 |
|
111 | Now, if you run that a second time, you'll get an error: the agent `'my_first_agent'` is already existing. Let's see how we can delete it before recreating it.
|
112 |
|
113 | ```js
|
114 | const AGENT_ID = 'my_first_agent';
|
115 |
|
116 | client.deleteAgent(AGENT_ID)
|
117 | .then(function() {
|
118 | console.log('Agent ' + AGENT_ID + ' no longer exists.');
|
119 | return client.createAgent(/*...*/);
|
120 | })
|
121 | .then(function(agent) {
|
122 | console.log('Agent ' + agent.id + ' successfully created!');
|
123 | })
|
124 | .catch(function(error) {
|
125 | console.error('Error!', error);
|
126 | });
|
127 | ```
|
128 |
|
129 | _For further information, check the ['create agent' reference documentation](#create)._
|
130 |
|
131 | ### 4 - Add context operations
|
132 |
|
133 | We have now created our first agent but it is not able to do much, yet. To learn a decision model it needs to be provided with data, in **craft ai** these are called context operations.
|
134 |
|
135 | Please note that only value changes are sent, thus if an operation doesn't contain a value, the previous known value is used.
|
136 |
|
137 | In the following we add 8 operations:
|
138 |
|
139 | 1. The initial one sets the initial state of the agent, on July 25 2016 at 5:30, in Paris, nobody is there and the light is off;
|
140 | 2. At 7:02, someone enters the room the light is turned on;
|
141 | 3. At 7:15, someone else enters the room;
|
142 | 4. At 7:31, the light is turned off;
|
143 | 5. At 8:12, everyone leaves the room;
|
144 | 6. At 19:23, 2 persons enter the room;
|
145 | 7. At 22:35, the light is turned on;
|
146 | 8. At 23:06, everyone leaves the room and the light is turned off.
|
147 |
|
148 |
|
149 | ```js
|
150 | const AGENT_ID = 'my_first_agent';
|
151 |
|
152 | // for the purpose of this test, we delete and recreate the agent
|
153 | client.deleteAgent(AGENT_ID)
|
154 | .then(function() {
|
155 | console.log('Agent ' + AGENT_ID + ' no longer exists.');
|
156 | return client.createAgent(/*...*/);
|
157 | })
|
158 | .then(function(agent) {
|
159 | console.log('Agent ' + agent.id + ' successfully created!');
|
160 | return client.addAgentContextOperations(
|
161 | AGENT_ID,
|
162 | [
|
163 | {
|
164 | timestamp: 1469410200,
|
165 | context: {
|
166 | timezone: '+02:00',
|
167 | peopleCount: 0,
|
168 | lightbulbState: 'OFF'
|
169 | }
|
170 | },
|
171 | {
|
172 | timestamp: 1469415720,
|
173 | context: {
|
174 | peopleCount: 1,
|
175 | lightbulbState: 'ON'
|
176 | }
|
177 | },
|
178 | {
|
179 | timestamp: 1469416500,
|
180 | context: {
|
181 | peopleCount: 2
|
182 | }
|
183 | },
|
184 | {
|
185 | timestamp: 1469417460,
|
186 | context: {
|
187 | lightbulbState: 'OFF'
|
188 | }
|
189 | },
|
190 | {
|
191 | timestamp: 1469419920,
|
192 | context: {
|
193 | peopleCount: 0
|
194 | }
|
195 | },
|
196 | {
|
197 | timestamp: 1469460180,
|
198 | context: {
|
199 | peopleCount: 2
|
200 | }
|
201 | },
|
202 | {
|
203 | timestamp: 1469471700,
|
204 | context: {
|
205 | lightbulbState: 'ON'
|
206 | }
|
207 | },
|
208 | {
|
209 | timestamp: 1469473560,
|
210 | context: {
|
211 | peopleCount: 0,
|
212 | lightbulbState: 'OFF'
|
213 | }
|
214 | }
|
215 | ]
|
216 | )
|
217 | .then(function() {
|
218 | return agent;
|
219 | });
|
220 | })
|
221 | .then(function(agent) {
|
222 | console.log('Successfully added initial operations to agent ' + agent.id + '.');
|
223 | })
|
224 | .catch(function(error) {
|
225 | console.error('Error!', error);
|
226 | });
|
227 | ```
|
228 |
|
229 | In real-world applications, you'll probably do the same kind of things when the agent is created and then, regularly throughout the lifetime of the agent with newer data.
|
230 |
|
231 | _For further information, check the ['add context operations' reference documentation](#add-operations)._
|
232 |
|
233 | ### 5 - Compute the decision tree
|
234 |
|
235 | The agent has acquired a context history, we can now compute a decision tree from it! A decision tree models the output, allowing us to estimate what the output would be in a given context.
|
236 |
|
237 | The decision tree is computed at a given timestamp, which means it will consider the context history from the creation of this agent up to this moment. Let's first try to compute the decision tree at midnight on July 26, 2016.
|
238 |
|
239 | ```js
|
240 | const AGENT_ID = 'my_first_agent';
|
241 |
|
242 | // for the purpose of this test, we delete and recreate the agent
|
243 | client.deleteAgent(AGENT_ID)
|
244 | .then(function() {
|
245 | console.log('Agent ' + AGENT_ID + ' no longer exists.');
|
246 | return client.createAgent(/*...*/);
|
247 | })
|
248 | .then(function(agent) {
|
249 | console.log('Agent ' + agent.id + ' successfully created!');
|
250 | return client.addAgentContextOperations(AGENT_ID, /*...*/)
|
251 | .then(function() {
|
252 | return agent;
|
253 | });
|
254 | })
|
255 | .then(function(agent) {
|
256 | console.log('Successfully added initial operations to agent ' + agent.id + '.');
|
257 | return client.getAgentDecisionTree(AGENT_ID, 1469476800);
|
258 | })
|
259 | .then(function(tree) {
|
260 | console.log('Decision tree retrieved!', tree);
|
261 | })
|
262 | .catch(function(error) {
|
263 | console.error('Error!', error);
|
264 | });
|
265 | ```
|
266 |
|
267 | Try to retrieve the tree at different timestamps to see how it gradually learns from the new operations. To visualize the trees, use the [inspector](https://beta.craft.ai/inspector)!
|
268 |
|
269 | _For further information, check the ['compute decision tree' reference documentation](#compute)._
|
270 |
|
271 | ### 6 - Take a decision
|
272 |
|
273 | Once the decision tree is computed it can be used to take a decision. In our case it is basically answering this type of question: "What is the anticipated **state of the lightbulb** at 7:15 if there are 2 persons in the room ?".
|
274 |
|
275 | ```js
|
276 | const AGENT_ID = 'my_first_agent';
|
277 |
|
278 | // for the purpose of this test, we delete and recreate the agent
|
279 | client.deleteAgent(AGENT_ID)
|
280 | .then(function() {
|
281 | console.log('Agent ' + AGENT_ID + ' no longer exists.');
|
282 | return client.createAgent(/*...*/);
|
283 | })
|
284 | .then(function(agent) {
|
285 | console.log('Agent ' + agent.id + ' successfully created!');
|
286 | return client.addAgentContextOperations(AGENT_ID, /*...*/)
|
287 | .then(function() {
|
288 | return agent;
|
289 | });
|
290 | })
|
291 | .then(function(agent) {
|
292 | console.log('Successfully added initial operations to agent ' + agent.id + '.');
|
293 | return client.getAgentDecisionTree(AGENT_ID, 1469476800);
|
294 | })
|
295 | .then(function(tree) {
|
296 | console.log('Decision tree retrieved!', tree);
|
297 | const res = craftai.interpreter.decide(tree, {
|
298 | timezone: '+02:00',
|
299 | timeOfDay: 7.25,
|
300 | peopleCount: 2
|
301 | });
|
302 | console.log('The anticipated lightbulb state is "' + res.output.lightbulbState.predicted_value + '".');
|
303 | })
|
304 | .catch(function(error) {
|
305 | console.error('Error!', error);
|
306 | });
|
307 | ```
|
308 |
|
309 | _For further information, check the ['take decision' reference documentation](#take-decision)._
|
310 |
|
311 | ### Node.JS starter kit ###
|
312 |
|
313 | If you prefer to get started from an existing code base, the official Node.JS starter kit can get you there! Retrieve the sources locally and follow the "readme" to get a fully working **SmartHome** app using _real-world_ data.
|
314 |
|
315 | > [:package: _Get the **craft ai** Node.JS Starter Kit_](https://github.com/craft-ai/craft-ai-starterkit-nodejs)
|
316 |
|
317 | ## API
|
318 |
|
319 | ### Project
|
320 |
|
321 | **craft ai** agents belong to **projects**. In the current version, each identified users defines a owner and can create projects for themselves, in the future we will introduce shared projects.
|
322 |
|
323 | ### Configuration
|
324 |
|
325 | Each agent has a configuration defining:
|
326 |
|
327 | - the context schema, i.e. the list of property keys and their type (as defined in the following section),
|
328 | - the output properties, i.e. the list of property keys on which the agent takes decisions,
|
329 |
|
330 | > :warning: In the current version, only one output property can be provided.
|
331 |
|
332 | - the `time_quantum`, i.e. the minimum amount of time, in seconds, that is meaningful for an agent; context updates occurring faster than this quantum won't be taken into account. As a rule of thumb, you should always choose the largest value that seems right and reduce it, if necessary, after some tests.
|
333 | - the `learning_period`, i.e. the maximum amount of time, in seconds, that matters for an agent; the agent's decision model can ignore context that is older than this duration. You should generally choose the smallest value that fits this description.
|
334 |
|
335 | > :warning: if no time_quantum is specified, the default value is 600.
|
336 |
|
337 | > :warning: if no learning_period is specified, the default value is 15000 time quantums.
|
338 |
|
339 | > :warning: the maximum learning_period value is 55000 \* time_quantum.
|
340 |
|
341 | #### Context properties types
|
342 |
|
343 | ##### Base types: `enum`, `continuous` and `boolean`
|
344 |
|
345 | `enum`, `continuous` and `boolean` are the three base **craft ai** types:
|
346 |
|
347 | - an `enum` property is a string;
|
348 | - a `continuous` property is a real number.
|
349 | - a `boolean` property is a boolean value: `true` or `false`
|
350 |
|
351 | > :warning: the absolute value of a `continuous` property must be less than 10<sup>20</sup>.
|
352 |
|
353 | #### Missing Values
|
354 |
|
355 | If one of these properties value is **missing**, this latter can be handled if the key *deactivate_missing_values: false* is added to the agent configuration. In this configuration, you can send a `null` value for a context attribute value to tell **craft ai** that the value is missing. **craft ai** will take into account as much as possible from this incomplete context.
|
356 |
|
357 | A context with a missing value looks like:
|
358 | ```json
|
359 | {
|
360 | "timestamp": 1469415720,
|
361 | "context": {
|
362 | "timezone": "+02:00",
|
363 | "temperature": null,
|
364 | "lightbulbState": "OFF"
|
365 | }
|
366 | }
|
367 | ```
|
368 |
|
369 | And its associated configuration would be:
|
370 | ```json
|
371 | {
|
372 | "context": {
|
373 | "timezone": {
|
374 | "type": "enum"
|
375 | },
|
376 | "temperature": {
|
377 | "type": "continuous"
|
378 | },
|
379 | "lightbulbState": {
|
380 | "type": "enum"
|
381 | }
|
382 | },
|
383 | "output": ["lightbulbState"],
|
384 | "time_quantum": 100,
|
385 | "learning_period": 108000,
|
386 | "deactivate_missing_values": false
|
387 | }
|
388 | ```
|
389 |
|
390 | #### Optional Values
|
391 |
|
392 | An `enum`, `continuous` or `boolean` property is defined as *optional* if this latter is explicitely known as being non applicable. For instance, a sensor measuring the ambient temperature can sometimes be *offline* on purpose, and this behavior must be considered as normal and not as a missing property. To tackle this kind of problem, we introduce *optional* values. A property is be defined as optional by adding `is_optional: true` to the types properties in your configuration. Then, in a context, an **optional** value is defined as `{}`, the empty JSON Object:
|
393 |
|
394 | A context with an optional value looks like:
|
395 | ```json
|
396 | {
|
397 | {
|
398 | "timestamp": 1469415720,
|
399 | "context": {
|
400 | "timezone": "+02:00",
|
401 | "temperature": {},
|
402 | "lightbulbState": "OFF"
|
403 | }
|
404 | },
|
405 | ...
|
406 | }
|
407 | ```
|
408 |
|
409 | And its associated configuration would be:
|
410 | ```json
|
411 | {
|
412 | "context": {
|
413 | "timezone": {
|
414 | "type": "enum"
|
415 | },
|
416 | "temperature": {
|
417 | "type": "continuous",
|
418 | "is_optional": true
|
419 | },
|
420 | "lightbulbState": {
|
421 | "type": "enum"
|
422 | }
|
423 | },
|
424 | "output": ["lightbulbState"],
|
425 | "time_quantum": 100,
|
426 | "learning_period": 108000,
|
427 | "deactivate_missing_values": false
|
428 | }
|
429 | ```
|
430 |
|
431 | ##### Time types: `timezone`, `time_of_day`, `day_of_week`, `day_of_month` and `month_of_year`
|
432 |
|
433 | **craft ai** defines the following types related to time:
|
434 |
|
435 | - a `time_of_day` property is a real number belonging to **[0.0; 24.0[**, each value represents the number of hours in the day since midnight (e.g. 13.5 means 13:30),
|
436 | - a `day_of_week` property is an integer belonging to **[0, 6]**, each value represents a day of the week starting from Monday (0 is Monday, 6 is Sunday).
|
437 | - a `day_of_month` property is an integer belonging to **[1, 31]**, each value represents a day of the month.
|
438 | - a `month_of_year` property is an integer belonging to **[1, 12]**, each value represents a month of the year.
|
439 | - a `timezone` property can be:
|
440 | * a string value representing the timezone as an offset from UTC, supported formats are:
|
441 |
|
442 | - **±[hh]:[mm]**,
|
443 | - **±[hh][mm]**,
|
444 | - **±[hh]**,
|
445 |
|
446 | where `hh` represent the hour and `mm` the minutes from UTC (eg. `+01:30`)), between `-12:00` and
|
447 | `+14:00`.
|
448 |
|
449 | * an integer belonging to **[-720, 840]** which represents the timezone as an offset from UTC:
|
450 |
|
451 | - in hours if the integer belongs to **[-15, 15]**
|
452 | - in minutes otherwise
|
453 |
|
454 | * an abbreviation among the following:
|
455 |
|
456 | - **UTC** or **Z** Universal Time Coordinated,
|
457 | - **GMT** Greenwich Mean Time, as UTC,
|
458 | - **BST** British Summer Time, as UTC+1 hour,
|
459 | - **IST** Irish Summer Time, as UTC+1,
|
460 | - **WET** Western Europe Time, as UTC,
|
461 | - **WEST** Western Europe Summer Time, as UTC+1,
|
462 | - **CET** Central Europe Time, as UTC+1,
|
463 | - **CEST** Central Europe Summer Time, as UTC+2,
|
464 | - **EET** Eastern Europe Time, as UTC+2,
|
465 | - **EEST** Eastern Europe Summer Time, as UTC+3,
|
466 | - **MSK** Moscow Time, as UTC+3,
|
467 | - **MSD** Moscow Summer Time, as UTC+4,
|
468 | - **AST** Atlantic Standard Time, as UTC-4,
|
469 | - **ADT** Atlantic Daylight Time, as UTC-3,
|
470 | - **EST** Eastern Standard Time, as UTC-5,
|
471 | - **EDT** Eastern Daylight Saving Time, as UTC-4,
|
472 | - **CST** Central Standard Time, as UTC-6,
|
473 | - **CDT** Central Daylight Saving Time, as UTC-5,
|
474 | - **MST** Mountain Standard Time, as UTC-7,
|
475 | - **MDT** Mountain Daylight Saving Time, as UTC-6,
|
476 | - **PST** Pacific Standard Time, as UTC-8,
|
477 | - **PDT** Pacific Daylight Saving Time, as UTC-7,
|
478 | - **HST** Hawaiian Standard Time, as UTC-10,
|
479 | - **AKST** Alaska Standard Time, as UTC-9,
|
480 | - **AKDT** Alaska Standard Daylight Saving Time, as UTC-8,
|
481 | - **AEST** Australian Eastern Standard Time, as UTC+10,
|
482 | - **AEDT** Australian Eastern Daylight Time, as UTC+11,
|
483 | - **ACST** Australian Central Standard Time, as UTC+9.5,
|
484 | - **ACDT** Australian Central Daylight Time, as UTC+10.5,
|
485 | - **AWST** Australian Western Standard Time, as UTC+8.
|
486 |
|
487 | > :information_source: By default, the values of the `time_of_day` and `day_of_week`
|
488 | > properties are generated from the [`timestamp`](#timestamp) of an agent's
|
489 | > state and the agent's current `timezone`. Therefore, whenever you use generated
|
490 | > `time_of_day` and/or `day_of_week` in your configuration, you **must** provide a
|
491 | > `timezone` value in the context. There can only be one `timezone` property.
|
492 | >
|
493 | > If you wish to provide their values manually, add `is_generated: false` to the
|
494 | > time types properties in your configuration. In this case, since you provide the values, the
|
495 | > `timezone` property is not required, and you must update the context whenever
|
496 | > one of these time values changes in a way that is significant for your system.
|
497 |
|
498 | ##### Examples
|
499 |
|
500 | Let's take a look at the following configuration. It is designed to model the **color** of a lightbulb (the `lightbulbColor` property, defined as an output) depending on the **outside light intensity** (the `lightIntensity` property), the **TV status** (the `TVactivated` property) the **time of the day** (the `time` property) and the **day of the week** (the `day` property). Since `TVactivated` doesn't make any sense if the TV isn't here, we also specify this property as `is_optional: true`.
|
501 |
|
502 | `day` and `time` values will be generated automatically, hence the need for
|
503 | `timezone`, the current Time Zone, to compute their value from given
|
504 | [`timestamps`](#timestamp).
|
505 |
|
506 | The `time_quantum` is set to 100 seconds, which means that if the lightbulb
|
507 | color is changed from red to blue then from blue to purple in less that 1
|
508 | minutes and 40 seconds, only the change from red to purple will be taken into
|
509 | account.
|
510 |
|
511 | The `learning_period` is set to 108 000 seconds (one month) , which means that
|
512 | the state of the lightbulb from more than a month ago can be ignored when learning
|
513 | the decision model.
|
514 |
|
515 | ```json
|
516 | {
|
517 | "context": {
|
518 | "lightIntensity": {
|
519 | "type": "continuous"
|
520 | },
|
521 | "TVactivated": {
|
522 | "type": "boolean",
|
523 | "is_optional": true
|
524 | },
|
525 | "time": {
|
526 | "type": "time_of_day"
|
527 | },
|
528 | "day": {
|
529 | "type": "day_of_week"
|
530 | },
|
531 | "timezone": {
|
532 | "type": "timezone"
|
533 | },
|
534 | "lightbulbColor": {
|
535 | "type": "enum"
|
536 | }
|
537 | },
|
538 | "output": ["lightbulbColor"],
|
539 | "time_quantum": 100,
|
540 | "learning_period": 108000
|
541 | }
|
542 | ```
|
543 |
|
544 | In this second example, the `time` property is not generated, no property of
|
545 | type `timezone` is therefore needed. However values of `time` must be manually
|
546 | provided continuously.
|
547 |
|
548 | ```json
|
549 | {
|
550 | "context": {
|
551 | "time": {
|
552 | "type": "time_of_day",
|
553 | "is_generated": false
|
554 | },
|
555 | "lightIntensity": {
|
556 | "type": "continuous"
|
557 | },
|
558 | "TVactivated": {
|
559 | "type": "boolean"
|
560 | },
|
561 | "lightbulbColor": {
|
562 | "type": "enum"
|
563 | }
|
564 | },
|
565 | "output": ["lightbulbColor"],
|
566 | "time_quantum": 100,
|
567 | "learning_period": 108000
|
568 | }
|
569 | ```
|
570 |
|
571 | ### Timestamp
|
572 |
|
573 | **craft ai** API heavily relies on `timestamps`. A `timestamp` is an instant represented as a [Unix time](https://en.wikipedia.org/wiki/Unix_time), that is to say the amount of seconds elapsed since Thursday, 1 January 1970 at midnight UTC. In most programming languages this representation is easy to retrieve, you can refer to [**this page**](https://github.com/techgaun/unix-time/blob/master/README.md) to find out how.
|
574 |
|
575 | #### `craftai.Time` ####
|
576 |
|
577 | The `craftai.Time` class facilitates the handling of time types in **craft ai**. It is able to extract the different **craft ai** formats from various _datetime_ representations, thanks to [Moment.js](http://momentjs.com).
|
578 |
|
579 | From a unix timestamp and an explicit UTC offset:
|
580 |
|
581 | ```js
|
582 | const t1 = new craftai.Time(1465496929, '+10:00');
|
583 |
|
584 | // t1 === {
|
585 | // utc: '2016-06-09T18:28:49.000Z',
|
586 | // timestamp: 1465496929,
|
587 | // day_of_week: 4,
|
588 | // day_of_month: 10,
|
589 | // month_of_year: 6,
|
590 | // time_of_day: 4.480277777777778,
|
591 | // timezone: '+10:00'
|
592 | // }
|
593 | ```
|
594 |
|
595 | From a unix timestamp and using the local UTC offset:
|
596 |
|
597 | ```js
|
598 | const t2 = new craftai.Time(1465496929);
|
599 |
|
600 | // Value are valid if in Paris !
|
601 | // t2 === {
|
602 | // utc: '2016-06-09T18:28:49.000Z',
|
603 | // timestamp: 1465496929,
|
604 | // day_of_week: 3,
|
605 | // day_of_month: 9,
|
606 | // month_of_year: 6,
|
607 | // time_of_day: 20.480277777777776,
|
608 | // timezone: '+02:00'
|
609 | // }
|
610 | ```
|
611 |
|
612 | From a [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) string:
|
613 |
|
614 | ```js
|
615 | const t3 = new craftai.Time('1977-04-22T01:00:00-05:00');
|
616 |
|
617 | // t3 === {
|
618 | // utc: '1977-04-22T06:00:00.000Z',
|
619 | // timestamp: 230536800,
|
620 | // day_of_week: 4,
|
621 | // time_of_day: 1,
|
622 | // day_of_month: 22,
|
623 | // month_of_year: 4,
|
624 | // timezone: '-05:00'
|
625 | // }
|
626 | ```
|
627 |
|
628 | From a [moment](http://momentjs.com) (or [moment timezone](http://momentjs.com/timezone/)) instance:
|
629 |
|
630 | ```js
|
631 | const t4 = new craftai.Time(moment.tz('2017-05-31 12:45:00', 'Asia/Dubai'), '-08:00'));
|
632 |
|
633 | // t4 === {
|
634 | // utc: '2017-05-31T08:45:00.000Z',
|
635 | // timestamp: 1496220300,
|
636 | // day_of_week: 2,
|
637 | // day_of_month: 31,
|
638 | // month_of_year: 5,
|
639 | // time_of_day: 0.75,
|
640 | // timezone: '-08:00'
|
641 | // }
|
642 | ```
|
643 |
|
644 | Retrieve the current time with the local UTC offset:
|
645 |
|
646 | ```js
|
647 | const now = new craftai.Time();
|
648 | ```
|
649 |
|
650 | Retrieve the current time with a given UTC offset:
|
651 |
|
652 | ```js
|
653 | const nowP5 = new craftai.Time(undefined, '+05:00');
|
654 | ```
|
655 |
|
656 | ### Advanced configuration
|
657 |
|
658 | The following **advanced** configuration parameters can be set in specific cases. They are **optional**. Usually you would not need them.
|
659 |
|
660 | - **`operations_as_events`** is a boolean, either `true` or `false`. The default value is `false`. If it is set to true, all context operations are treated as events, as opposed to context updates. This is appropriate if the data for an agent is made of events that have no duration, and if many events are more significant than a few. If `operations_as_events` is `true`, `learning_period` and the advanced parameter `tree_max_operations` must be set as well. In that case, `time_quantum` is ignored because events have no duration, as opposed to the evolution of an agent's context over time.
|
661 | - **`tree_max_operations`** is a positive integer. It **can and must** be set only if `operations_as_events` is `true`. It defines the maximum number of events on which a single decision tree can be based. It is complementary to `learning_period`, which limits the maximum age of events on which a decision tree is based.
|
662 | - **`tree_max_depth`** is a positive integer. It defines the maximum depth of decision trees, which is the maximum distance between the root node and a leaf (terminal) node. A depth of 0 means that the tree is made of a single root node. By default, `tree_max_depth` is set to 6 if the output is categorical (e.g. `enum`), or to 4 if the output is numerical (e.g. `continuous`).
|
663 |
|
664 | These advanced configuration parameters are optional, and will appear in the agent information returned by **craft ai** only if you set them to something other than their default value. If you intend to use them in a production environment, please get in touch with us.
|
665 |
|
666 | ### Agent
|
667 |
|
668 | #### Create
|
669 |
|
670 | Create a new agent, and create its [configuration](#configuration).
|
671 |
|
672 | > The agent's identifier is a case sensitive string between 1 and 36 characters long. It only accepts letters, digits, hyphen-minuses and underscores (i.e. the regular expression `/[a-zA-Z0-9_-]{1,36}/`).
|
673 |
|
674 | ```js
|
675 | client.createAgent(
|
676 | { // The configuration
|
677 | context: {
|
678 | peopleCount: {
|
679 | type: 'continuous'
|
680 | },
|
681 | timeOfDay: {
|
682 | type: 'time_of_day'
|
683 | },
|
684 | timezone: {
|
685 | type: 'timezone'
|
686 | },
|
687 | lightbulbState: {
|
688 | type: 'enum'
|
689 | }
|
690 | },
|
691 | output: [ 'lightbulbState' ],
|
692 | time_quantum: 100,
|
693 | learning_period: 108000
|
694 | },
|
695 | 'impervious_kraken' // id for the agent, if undefined a random id is generated
|
696 | )
|
697 | .then(function(agent) {
|
698 | // Work on the agent here
|
699 | // agent = {
|
700 | // "_version": <version>
|
701 | // "id": <agent_id>,
|
702 | // "configuration": {
|
703 | // "context": {
|
704 | // "peopleCount": {
|
705 | // "type": "continuous"
|
706 | // },
|
707 | // "timeOfDay": {
|
708 | // "type": "time_of_day"
|
709 | // },
|
710 | // "timezone": {
|
711 | // "type": "timezone"
|
712 | // },
|
713 | // "lightbulbState": {
|
714 | // "type": "enum"
|
715 | // }
|
716 | // },
|
717 | // "output": [ "lightbulbState" ],
|
718 | // "time_quantum": 100,
|
719 | // "learning_period": 108000
|
720 | // }
|
721 | // }
|
722 | })
|
723 | .catch(function(error) {
|
724 | // Catch errors here
|
725 | })
|
726 | ```
|
727 |
|
728 | #### Delete
|
729 |
|
730 | ```js
|
731 | client.deleteAgent(
|
732 | 'impervious_kraken' // The agent id
|
733 | )
|
734 | .then(function() {
|
735 | // The agent was successfully deleted
|
736 | })
|
737 | .catch(function(error) {
|
738 | // Catch errors here
|
739 | })
|
740 | ```
|
741 |
|
742 | #### Retrieve
|
743 |
|
744 | ```js
|
745 | client.getAgent(
|
746 | 'impervious_kraken' // The agent id
|
747 | )
|
748 | .then(function(agent) {
|
749 | // Agent details
|
750 | })
|
751 | .catch(function(error) {
|
752 | // Catch errors here
|
753 | })
|
754 | ```
|
755 |
|
756 | #### List
|
757 |
|
758 | ```js
|
759 | client.listAgents()
|
760 | .then(function(agentIds) {
|
761 | // list of agent ids, eg. ['impervious_kraken', 'impervious_kraken']
|
762 | })
|
763 | .catch(function(error) {
|
764 | // Catch errors here
|
765 | })
|
766 | ```
|
767 |
|
768 | #### Create and retrieve shared url
|
769 |
|
770 | Create and get a shareable url to view an agent tree.
|
771 | Only one url can be created at a time.
|
772 |
|
773 | ```js
|
774 | client.getSharedAgentInspectorUrl(
|
775 | 'impervious_kraken', // The agent id.
|
776 | 1464600256 // optional, the timestamp for which you want to inspect the tree.
|
777 | )
|
778 | .then(function(url) {
|
779 | // Url to the agent's inspector
|
780 | })
|
781 | .catch(function(error) {
|
782 | // Catch errors here
|
783 | })
|
784 | ```
|
785 |
|
786 | #### Delete shared url
|
787 |
|
788 | Delete a shareable url.
|
789 | The previous url cannot access the agent tree anymore.
|
790 |
|
791 | ```js
|
792 | client.deleteSharedAgentInspectorUrl(
|
793 | 'impervious_kraken' // The agent id.
|
794 | )
|
795 | .then(function() {
|
796 | // return nothing
|
797 | })
|
798 | .catch(function(error) {
|
799 | // Catch errors here
|
800 | })
|
801 | ```
|
802 |
|
803 |
|
804 |
|
805 | ### Context
|
806 |
|
807 | #### Add operations
|
808 |
|
809 | ```js
|
810 | client.addAgentContextOperations(
|
811 | 'impervious_kraken', // The agent id
|
812 | [ // The list of operations
|
813 | {
|
814 | timestamp: 1469410200, // Operation timestamp
|
815 | context: {
|
816 | timezone: '+02:00',
|
817 | peopleCount: 0,
|
818 | lightbulbState: 'OFF'
|
819 | }
|
820 | },
|
821 | {
|
822 | timestamp: 1469415720,
|
823 | context: {
|
824 | peopleCount: 1,
|
825 | lightbulbState: 'ON'
|
826 | }
|
827 | },
|
828 | {
|
829 | timestamp: 1469416500,
|
830 | context: {
|
831 | peopleCount: 2
|
832 | }
|
833 | },
|
834 | {
|
835 | timestamp: 1469417460,
|
836 | context: {
|
837 | lightbulbState: 'OFF'
|
838 | }
|
839 | },
|
840 | {
|
841 | timestamp: 1469419920,
|
842 | context: {
|
843 | peopleCount: 0
|
844 | }
|
845 | },
|
846 | {
|
847 | timestamp: 1469460180,
|
848 | context: {
|
849 | peopleCount: 2
|
850 | }
|
851 | },
|
852 | {
|
853 | timestamp: 1469471700,
|
854 | context: {
|
855 | lightbulbState: 'ON'
|
856 | }
|
857 | },
|
858 | {
|
859 | timestamp: 1469473560,
|
860 | context: {
|
861 | peopleCount: 0
|
862 | }
|
863 | }
|
864 | ]
|
865 | )
|
866 | .then(function() {
|
867 | // The operations where successfully added to agent context on the server side
|
868 | })
|
869 | .catch(function(error) {
|
870 | // Catch errors here
|
871 | })
|
872 | ```
|
873 |
|
874 | #### List operations
|
875 |
|
876 | ```js
|
877 | client.getAgentContextOperations(
|
878 | 'impervious_kraken', // The agent id
|
879 | 1478894153, // Optional, the **start** timestamp from which the
|
880 | // operations are retrieved (inclusive bound)
|
881 | 1478895266, // Optional, the **end** timestamp up to which the
|
882 | /// operations are retrieved (inclusive bound)
|
883 | )
|
884 | .then(function(operations) {
|
885 | // Work on operations
|
886 | })
|
887 | .catch(function(error) {
|
888 | // Catch errors here
|
889 | })
|
890 | ```
|
891 |
|
892 | > This call can generate multiple requests to the craft ai API as results are paginated.
|
893 |
|
894 | #### Retrieve state
|
895 |
|
896 | ```js
|
897 | client.getAgentContext(
|
898 | 'impervious_kraken', // The agent id
|
899 | 1469473600 // The timestamp at which the context state is retrieved
|
900 | )
|
901 | .then(function(context) {
|
902 | // Work on context
|
903 | })
|
904 | .catch(function(error) {
|
905 | // Catch errors here
|
906 | })
|
907 | ```
|
908 |
|
909 | #### Retrieve state history
|
910 |
|
911 | ```js
|
912 | client.getAgentStateHistory(
|
913 | 'impervious_kraken', // The agent id
|
914 | 1478894153, // Optional, the **start** timestamp from which the
|
915 | // operations are retrieved (inclusive bound)
|
916 | 1478895266, // Optional, the **end** timestamp up to which the
|
917 | /// operations are retrieved (inclusive bound)
|
918 | )
|
919 | .then(function(stateHistory) {
|
920 | // Work on states history
|
921 | })
|
922 | .catch(function(error) {
|
923 | // Catch errors here
|
924 | })
|
925 | ```
|
926 |
|
927 | ### Decision tree
|
928 |
|
929 | Decision trees are computed at specific timestamps, directly by **craft ai** which learns from the context operations [added](#add-operations) throughout time.
|
930 |
|
931 | When you [compute](#compute) a decision tree, **craft ai** returns an object containing:
|
932 |
|
933 | - the **API version**
|
934 | - the agent's configuration as specified during the agent's [creation](#create-agent)
|
935 | - the tree itself as a JSON object:
|
936 |
|
937 | - Internal nodes are represented by a `"decision_rule"` object and a `"children"` array. The first one, contains the `"property`, and the `"property"`'s value, to decide which child matches a context.
|
938 | - Leaves have a `"predicted_value"`, `"confidence"` and `"decision_rule"` object for this value, instead of a `"children"` array. `"predicted_value`" is an estimation of the output in the contexts matching the node. `"confidence"` is a number between 0 and 1 that indicates how confident **craft ai** is that the output is a reliable prediction. When the output is a numerical type, leaves also have a `"standard_deviation"` that indicates a margin of error around the `"predicted_value"`.
|
939 | - The root only contains a `"children"` array.
|
940 |
|
941 | #### Compute
|
942 |
|
943 | ```js
|
944 | client.getAgentDecisionTree(
|
945 | 'impervious_kraken', // The agent id
|
946 | 1469473600 // The timestamp at which the decision tree is retrieved
|
947 | )
|
948 | .then(function(tree) {
|
949 | // Works with the given tree
|
950 | console.log(tree);
|
951 | /* Outputted tree is the following
|
952 | {
|
953 | "_version":"2.0.0",
|
954 | "trees":{
|
955 | "lightbulbState":{
|
956 | "output_values":["OFF", "ON"],
|
957 | "children":[
|
958 | {
|
959 | "children":[
|
960 | {
|
961 | "prediction":{
|
962 | "confidence":0.6774609088897705,
|
963 | "distribution":[0.8, 0.2],
|
964 | "value":"OFF",
|
965 | "nb_samples": 5
|
966 | },
|
967 | "decision_rule":{
|
968 | "operand":0.5,
|
969 | "operator":"<",
|
970 | "property":"peopleCount"
|
971 | }
|
972 | },
|
973 | {
|
974 | "prediction":{
|
975 | "confidence":0.8630361557006836,
|
976 | "distribution":[0.1, 0.9],
|
977 | "value":"ON",
|
978 | "nb_samples": 10
|
979 | },
|
980 | "decision_rule":{
|
981 | "operand":0.5,
|
982 | "operator":">=",
|
983 | "property":"peopleCount"
|
984 | }
|
985 | }
|
986 | ],
|
987 | "decision_rule":{
|
988 | "operand":[
|
989 | 5,
|
990 | 5.6666665
|
991 | ],
|
992 | "operator":"[in[",
|
993 | "property":"timeOfDay"
|
994 | }
|
995 | },
|
996 | {
|
997 | "children":[
|
998 | {
|
999 | "prediction":{
|
1000 | "confidence":0.9947378635406494,
|
1001 | "distribution":[1.0, 0.0],
|
1002 | "value":"ON",
|
1003 | "nb_samples": 10
|
1004 | },
|
1005 | "decision_rule":{
|
1006 | "operand":[
|
1007 | 5.6666665,
|
1008 | 20.666666
|
1009 | ],
|
1010 | "operator":"[in[",
|
1011 | "property":"timeOfDay"
|
1012 | }
|
1013 | },
|
1014 | {
|
1015 | "children":[
|
1016 | {
|
1017 | "prediction":{
|
1018 | "confidence":0.969236433506012,
|
1019 | "distribution":[0.95, 0.05],
|
1020 | "value":"OFF",
|
1021 | "nb_samples": 10
|
1022 | },
|
1023 | "decision_rule":{
|
1024 | "operand":1,
|
1025 | "operator":"<",
|
1026 | "property":"peopleCount"
|
1027 | }
|
1028 | },
|
1029 | {
|
1030 | "prediction":{
|
1031 | "confidence":0.8630361557006836,
|
1032 | "distribution":[0.2, 0.8],
|
1033 | "value":"ON",
|
1034 | "nb_samples": 15
|
1035 | },
|
1036 | "decision_rule":{
|
1037 | "operand":1,
|
1038 | "operator":">=",
|
1039 | "property":"peopleCount"
|
1040 | }
|
1041 | }
|
1042 | ],
|
1043 | "decision_rule":{
|
1044 | "operand":[
|
1045 | 20.666666,
|
1046 | 5
|
1047 | ],
|
1048 | "operator":"[in[",
|
1049 | "property":"timeOfDay"
|
1050 | }
|
1051 | }
|
1052 | ],
|
1053 | "decision_rule":{
|
1054 | "operand":[
|
1055 | 5.6666665,
|
1056 | 5
|
1057 | ],
|
1058 | "operator":"[in[",
|
1059 | "property":"timeOfDay"
|
1060 | }
|
1061 | }
|
1062 | ]
|
1063 | }
|
1064 | },
|
1065 | "configuration":{
|
1066 | "time_quantum":600,
|
1067 | "learning_period":9000000,
|
1068 | "context":{
|
1069 | "peopleCount":{
|
1070 | "type":"continuous"
|
1071 | },
|
1072 | "timeOfDay":{
|
1073 | "type":"time_of_day",
|
1074 | "is_generated":true
|
1075 | },
|
1076 | "timezone":{
|
1077 | "type":"timezone"
|
1078 | },
|
1079 | "lightbulbState":{
|
1080 | "type":"enum"
|
1081 | }
|
1082 | },
|
1083 | "output":[
|
1084 | "lightbulbState"
|
1085 | ]
|
1086 | }
|
1087 | }
|
1088 | */
|
1089 | })
|
1090 | .catch(function(error) {
|
1091 | if (error instanceof craftai.errors.CraftAiLongRequestTimeOutError) {
|
1092 | // Handle timeout errors here
|
1093 | }
|
1094 | else {
|
1095 | // Handle other errors here
|
1096 | }
|
1097 | })
|
1098 | ```
|
1099 |
|
1100 | #### Take decision
|
1101 |
|
1102 | > :information_source: To take a decision, first compute the decision tree then use the **offline interpreter**.
|
1103 |
|
1104 | ### Bulk
|
1105 |
|
1106 | The craft ai API includes a bulk route which provides a programmatic option to perform asynchronous operations on agents. It allows the user to create, delete, add operations and compute decision trees for several agents at once.
|
1107 |
|
1108 | > :warning: the bulk API is a quite advanced feature. It comes on top of the basic routes to create, delete, add context operations and compute decision tree. If messages are not self-explanatory, please refer to the basic routes that does the same operation for a single agent.
|
1109 |
|
1110 |
|
1111 |
|
1112 | #### Bulk - Create
|
1113 |
|
1114 | To create several agents at once, use the method `createAgentBulk` as the following:
|
1115 |
|
1116 | ```js
|
1117 | const agent_ID_1 = 'my_first_agent';
|
1118 | const agent_ID_2 = 'my_second_agent';
|
1119 |
|
1120 | const configuration_1 = {
|
1121 | context: {
|
1122 | peopleCount: {
|
1123 | type: 'continuous'
|
1124 | },
|
1125 | timeOfDay: {
|
1126 | type: 'time_of_day'
|
1127 | },
|
1128 | timezone: {
|
1129 | type: 'timezone'
|
1130 | },
|
1131 | lightbulbState: {
|
1132 | type: 'enum'
|
1133 | }
|
1134 | },
|
1135 | output: [ 'lightbulbState' ]
|
1136 | };
|
1137 | const configuration_2 = { /* ... */ };
|
1138 |
|
1139 | const createBulkPayload = [
|
1140 | {id: agent_ID_1, configuration: configuration_1},
|
1141 | {id: agent_ID_2, configuration: configuration_2}
|
1142 | ];
|
1143 |
|
1144 | client.createAgentBulk(createBulkPayload)
|
1145 | .then(function(agents) {
|
1146 | console.log(agents);
|
1147 | })
|
1148 | .catch(function(error) {
|
1149 | console.error('Error!', error);
|
1150 | })
|
1151 | ```
|
1152 |
|
1153 | The variable `agents` is an **array of responses**. If an agent has been successfully created, the corresponding response is an object similar to the classic `createAgent()` response. When there are **mixed results**, `agents` should looks like:
|
1154 |
|
1155 | ```js
|
1156 | [
|
1157 | { id: 'my_first_agent', // creation failed
|
1158 | status: 400,
|
1159 | error: 'errorId',
|
1160 | message: 'error-message' },
|
1161 | { configuration: // creation succeed
|
1162 | { time_quantum: 100,
|
1163 | learning_period: 1500000,
|
1164 | context: [Object],
|
1165 | output: [Object] },
|
1166 | id: 'my_second_agent',
|
1167 | _version: '2.0.0' }
|
1168 | ]
|
1169 | ```
|
1170 |
|
1171 | #### Bulk - Delete
|
1172 |
|
1173 | ```js
|
1174 | const agent_ID_1 = 'my_first_agent';
|
1175 | const agent_ID_2 = 'my_second_agent';
|
1176 |
|
1177 | const deleteBulkPayload = [
|
1178 | { id: agent_ID_1 },
|
1179 | { id: agent_ID_2 }
|
1180 | ];
|
1181 |
|
1182 | client.deleteAgentBulk(deleteBulkPayload)
|
1183 | .then(function(deletedAgents) {
|
1184 | console.log(agents);
|
1185 | })
|
1186 | .catch(function(error) {
|
1187 | console.error('Error!', error);
|
1188 | });
|
1189 | ```
|
1190 | The variable `deletedAgents` is an **array of responses**. If an agent has been successfully deleted, the corresponding response is an object similar to the classic `deleteAgent()` response. When there are **mixed results**, `deletedAgents` should looks like:
|
1191 |
|
1192 | ```js
|
1193 | [
|
1194 | { id: 'my_first_agent', // deletion succeed
|
1195 | configuration:
|
1196 | { time_quantum: 100,
|
1197 | learning_period: 1500000,
|
1198 | context: [Object],
|
1199 | output: [Object] },
|
1200 | creationDate: 1557492944277,
|
1201 | lastContextUpdate: 1557492944277,
|
1202 | lastTreeUpdate: 1557492944277,
|
1203 | _version: '2.0.0' },
|
1204 | { id: 'my_unknown_agent' }, // deletion succeed
|
1205 | { id: 'my_second_agent', // deletion failed
|
1206 | status: 400,
|
1207 | error: 'errorId',
|
1208 | message: 'error-message' }
|
1209 | ]
|
1210 | ```
|
1211 |
|
1212 | #### Bulk - Add context Operations
|
1213 |
|
1214 | ```js
|
1215 | const agent_ID_1 = 'my_first_agent';
|
1216 | const agent_ID_2 = 'my_second_agent';
|
1217 |
|
1218 | const operations_agent_1 = [{
|
1219 | timestamp: 1469410200,
|
1220 | context: {
|
1221 | timezone: '+02:00',
|
1222 | peopleCount: 0,
|
1223 | lightbulbState: 'OFF'
|
1224 | }
|
1225 | },
|
1226 | {
|
1227 | timestamp: 1469415720,
|
1228 | context: {
|
1229 | peopleCount: 1,
|
1230 | lightbulbState: 'ON'
|
1231 | }
|
1232 | },
|
1233 | {
|
1234 | timestamp: 1469416500,
|
1235 | context: {
|
1236 | peopleCount: 2
|
1237 | }
|
1238 | },
|
1239 | {
|
1240 | timestamp: 1469417460,
|
1241 | context: {
|
1242 | lightbulbState: 'OFF'
|
1243 | }
|
1244 | }];
|
1245 | const operations_agent_2 = [ /* ... */ ];
|
1246 |
|
1247 | const contextOperationBulkPayload = [
|
1248 | { id: agent_ID_1, operations: operations_agent_1},
|
1249 | { id: agent_ID_2, operations: operations_agent_2}
|
1250 | ];
|
1251 |
|
1252 | client.addAgentContextOperationsBulk(contextOperationBulkPayload)
|
1253 | .then(function(agents) {
|
1254 | console.log(agents);
|
1255 | })
|
1256 | .catch(function(error) {
|
1257 | console.error('Error!', error);
|
1258 | });
|
1259 | ```
|
1260 |
|
1261 | The variable `agents` is an **array of responses**. If an agent has successfully received operations, the corresponding response is an object similar to the classic `addAgentContextOperations()` response. When there are **mixed results**, `agents` should looks like:
|
1262 |
|
1263 | ```js
|
1264 | [
|
1265 | { id: 'my_first_agent', // add operations failed
|
1266 | status: 500,
|
1267 | error: 'errorId',
|
1268 | message: 'error-message' },
|
1269 | { id: 'my_second_agent', // add operations succeed
|
1270 | message: 'Successfully added XX operation(s) to the agent "{owner}/{project}/my_second_agent" context.',
|
1271 | status: 201 }
|
1272 | ]
|
1273 | ```
|
1274 |
|
1275 | #### Bulk - Compute decision trees
|
1276 |
|
1277 | ```js
|
1278 | const agent_ID_1 = 'my_first_agent';
|
1279 | const agent_ID_2 = 'my_second_agent';
|
1280 |
|
1281 | const decisionTreePayload = [
|
1282 | { id: agent_ID_1 },
|
1283 | { id: agent_ID_2 }
|
1284 | ];
|
1285 |
|
1286 | client.getAgentDecisionTreeBulk(decisionTreePayload)
|
1287 | .then(function(trees) {
|
1288 | console.log(trees);
|
1289 | })
|
1290 | .catch(function(error) {
|
1291 | console.error('Error!', error);
|
1292 | });
|
1293 | ```
|
1294 |
|
1295 | The variable `trees` is an **array of responses**. If an agent's model has successfully been created, the corresponding response is an object similar to the classic `getAgentDecisionTree()` response. When there are **mixed results**, `trees` should looks like:
|
1296 |
|
1297 | ```js
|
1298 | [
|
1299 | { id: 'my_first_agent', // computation failed
|
1300 | (...)
|
1301 | status: 400,
|
1302 | error: 'errorId',
|
1303 | message: 'error-message' },
|
1304 | { id: 'my_second_agent', // computation succeed
|
1305 | timestamp: 1464601500,
|
1306 | tree: { _version: '1.1.0', trees: [Object], configuration: [Object] } }
|
1307 | ]
|
1308 | ```
|
1309 |
|
1310 | ### Advanced client configuration ###
|
1311 |
|
1312 | The simple configuration to create the `client` is just the token. For special needs, additional advanced configuration can be provided.
|
1313 |
|
1314 | #### Amount of operations sent in one chunk ####
|
1315 |
|
1316 | `client.addAgentContextOperations` splits the provided operations into chunks in order to limit the size of the http requests to the craft ai API. In the client configuration, `operationsChunksSize` can be increased in order to limit the number of request, or decreased when large http requests cause errors.
|
1317 |
|
1318 | ```js
|
1319 | const client = craftai({
|
1320 | // Mandatory, the token
|
1321 | token: '{token}',
|
1322 | // Optional, default value is 500
|
1323 | operationsChunksSize: {max_number_of_operations_sent_at_once}
|
1324 | });
|
1325 | ```
|
1326 |
|
1327 | #### Timeout duration for decision trees retrieval ####
|
1328 |
|
1329 | It is possible to increase or decrease the timeout duration of `client.getAgentDecisionTree`, for exemple to account for especially long computations.
|
1330 |
|
1331 | ```js
|
1332 | const client = craftai({
|
1333 | // Mandatory, the token
|
1334 | token: '{token}',
|
1335 | // Optional, default value is 5 minutes (300000)
|
1336 | decisionTreeRetrievalTimeout: {timeout_duration_for_decision_trees_retrieval}
|
1337 | });
|
1338 | ```
|
1339 |
|
1340 | #### Proxy ####
|
1341 |
|
1342 | > :information_source: This setting can only be set in Node.js environements. In a browser environement the settings of the browser will be used automatically.
|
1343 |
|
1344 | It is possible to provide proxy configuration in the `proxy` property of the client configuration. It will be used to call the craft ai API (through HTTPS). The expected format is a host name or IP and port, optionally preceded by credentials such as `http://user:pass@10.10.1.10:1080`.
|
1345 |
|
1346 | ```js
|
1347 | const client = craftai({
|
1348 | // Mandatory, the token
|
1349 | token: '{token}',
|
1350 | // Optional, no default value
|
1351 | proxy: 'http://{user}:{password}@{host_or_ip}:{port}'
|
1352 | });
|
1353 | ```
|
1354 | ## Interpreter ##
|
1355 |
|
1356 | The decision tree interpreter can be used offline from decisions tree computed through the API.
|
1357 |
|
1358 | ### Take decision ###
|
1359 |
|
1360 | ```js
|
1361 | // `tree` is the decision tree as retrieved through the craft ai REST API
|
1362 | const tree = { ... };
|
1363 | // Compute the decision with specifying every context field
|
1364 | const decision = craftai.interpreter.decide(
|
1365 | tree,
|
1366 | {
|
1367 | timezone: '+02:00',
|
1368 | timeOfDay: 7.5,
|
1369 | peopleCount: 3
|
1370 | });
|
1371 |
|
1372 | // Or Compute the decision on a context created from the given one and filling the
|
1373 | // `day_of_week`, `time_of_day` and `timezone` properties from the given `Time`
|
1374 | const decision = craftai.interpreter.decide(
|
1375 | tree,
|
1376 | {
|
1377 | timezone: '+02:00',
|
1378 | peopleCount: 3
|
1379 | },
|
1380 | new craftai.Time('2010-01-01T07:30:30'));
|
1381 | ```
|
1382 |
|
1383 | > Any number of partial contexts and/or `craftai.Time` instances can be provided to `decide`, it follows the same semantics as [Object.assign(...)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign): the later arguments overriding the properties value from the previous ones)
|
1384 |
|
1385 | A computed `decision` on an `enum` type would look like:
|
1386 |
|
1387 | ```js
|
1388 | {
|
1389 | context: { // In which context the decision was taken
|
1390 | timezone: '+02:00',
|
1391 | timeOfDay: 7.5,
|
1392 | peopleCount: 3
|
1393 | },
|
1394 | output: { // The decision itself
|
1395 | lightbulbState: {
|
1396 | predicted_value: 'ON',
|
1397 | confidence: 0.9937745256361138, // The confidence in the decision
|
1398 | decision_rules: [ // The ordered list of decision_rules that were validated to reach this decision
|
1399 | {
|
1400 | property: 'timeOfDay',
|
1401 | operator: '>=',
|
1402 | operand: 6
|
1403 | },
|
1404 | {
|
1405 | property: 'peopleCount',
|
1406 | operator: '>=',
|
1407 | operand: 2
|
1408 | }
|
1409 | ],
|
1410 | nb_samples: 25,
|
1411 | distribution: [0.05, 0.95]
|
1412 | }
|
1413 | }
|
1414 | }
|
1415 | ```
|
1416 |
|
1417 | A `decision` for a numerical output type would look like:
|
1418 |
|
1419 | ```js
|
1420 | output: {
|
1421 | lightbulbState: {
|
1422 | predicted_value: "OFF",
|
1423 | confidence: ...,
|
1424 | distribution: [ ... ],
|
1425 | nb_samples: 25,
|
1426 | decision_rules: [ ... ]
|
1427 | }
|
1428 | }
|
1429 | ```
|
1430 |
|
1431 | A `decision` for a categorical output type would look like:
|
1432 |
|
1433 | ```js
|
1434 | output: {
|
1435 | lightbulbIntensity: {
|
1436 | predicted_value: 10.5,
|
1437 | standard_deviation: 1.25,
|
1438 | confidence: ...,
|
1439 | min: 8.0,
|
1440 | max: 11,
|
1441 | nb_samples: 25,
|
1442 | decision_rules: [ ... ]
|
1443 | }
|
1444 | }
|
1445 | ```
|
1446 |
|
1447 |
|
1448 | A `decision` in a case where the tree cannot make a prediction:
|
1449 |
|
1450 | ```js
|
1451 | decision: {
|
1452 | lightbulbState: {
|
1453 | predicted_value: null, // No decision
|
1454 | distribution : [ ... ], // Distribution of the output classes normalized by the number of samples in the reached node.
|
1455 | confidence: 0, // Zero confidence if the decision is null
|
1456 | nb_samples: 25,
|
1457 | decision_rules: [ ... ]
|
1458 | }
|
1459 | },
|
1460 | ```
|
1461 |
|
1462 | ### Take multiple decisions ###
|
1463 |
|
1464 | From the tree previously retrieved, ask for multiple decisions.
|
1465 |
|
1466 | ```js
|
1467 | // `tree` is the decision tree as retrieved through the craft ai REST API
|
1468 | const tree = { ... };
|
1469 | // Pass an array containing each context on which you want to take a decision
|
1470 | const decisions = craftai.interpreter.decideFromContextsArray(tree, [
|
1471 | {
|
1472 | timezone: '+02:00',
|
1473 | peopleCount: 3,
|
1474 | timeOfDay: 7.5
|
1475 | },
|
1476 | {
|
1477 | timezone: '+02:00',
|
1478 | peopleCount: 4,
|
1479 | timeOfDay: 7.5
|
1480 | },
|
1481 | {
|
1482 | timezone: '+02:00',
|
1483 | peopleCount: 0,
|
1484 | timeOfDay: 4.5
|
1485 | }
|
1486 | ])
|
1487 | ```
|
1488 |
|
1489 | Results for `craftai.interpreter.decideFromContextsArray` would look like:
|
1490 |
|
1491 | ```js
|
1492 | [
|
1493 | {
|
1494 | context: { // In which context the decision was taken
|
1495 | timezone: '+02:00',
|
1496 | timeOfDay: 7.5,
|
1497 | peopleCount: 3
|
1498 | },
|
1499 | output: { // The decision itself
|
1500 | lightbulbState: {
|
1501 | predicted_value: 'ON',
|
1502 | distribution: [0.0, 1.0],
|
1503 | nb_samples: 20,
|
1504 | confidence: 0.9937745256361138, // The confidence in the decision
|
1505 | decision_rules: [ // The ordered list of decision_rules that were validated to reach this decision
|
1506 | {
|
1507 | property: 'timeOfDay',
|
1508 | operator: '>=',
|
1509 | operand: 6
|
1510 | },
|
1511 | {
|
1512 | property: 'peopleCount',
|
1513 | operator: '>=',
|
1514 | operand: 2
|
1515 | }
|
1516 | ]
|
1517 | }
|
1518 | }
|
1519 | },
|
1520 | {
|
1521 | context: {
|
1522 | timezone: '+02:00',
|
1523 | timeOfDay: 7.5,
|
1524 | peopleCount: 4
|
1525 | },
|
1526 | output: {
|
1527 | lightbulbState: {
|
1528 | predicted_value: 'ON',
|
1529 | distribution: [0.0, 1.0],
|
1530 | nb_samples: 20,
|
1531 | confidence: 0.9937745256361138,
|
1532 | decision_rules: [
|
1533 | {
|
1534 | property: 'timeOfDay',
|
1535 | operator: '>=',
|
1536 | operand: 6
|
1537 | },
|
1538 | {
|
1539 | property: 'peopleCount',
|
1540 | operator: '>=',
|
1541 | operand: 2
|
1542 | }
|
1543 | ]
|
1544 | }
|
1545 | }
|
1546 | },
|
1547 | {
|
1548 | context: {
|
1549 | timezone: '+02:00',
|
1550 | timeOfDay: 4.5,
|
1551 | peopleCount: 0
|
1552 | },
|
1553 | output: {
|
1554 | lightbulbState: {
|
1555 | predicted_value: 'OFF',
|
1556 | distribution: [0.95, 0.05],
|
1557 | nb_samples: 12,
|
1558 | confidence: 0.9545537233352661,
|
1559 | decision_rules: [ // The ordered list of decision_rules that were validated to reach this decision
|
1560 | {
|
1561 | property: 'timeOfDay',
|
1562 | operator: '<',
|
1563 | operand: 5.666666507720947
|
1564 | },
|
1565 | {
|
1566 | property: 'peopleCount',
|
1567 | operator: '<',
|
1568 | operand: 1
|
1569 | }
|
1570 | ]
|
1571 | }
|
1572 | }
|
1573 | }
|
1574 | ]
|
1575 | ```
|
1576 |
|
1577 | ### Reduce decision rules ###
|
1578 |
|
1579 | From a list of decision rules, as retrieved when taking a decision, when taking a decision compute an equivalent & minimal list of rules.
|
1580 |
|
1581 | ```js
|
1582 | // `decision` is the decision tree as retrieved from taking a decision
|
1583 | const decision = craftai.interpreter.decide( ... );
|
1584 |
|
1585 | // `decisionRules` is the decision rules that led to decision for the `lightBulbState` value
|
1586 | const decisionRules = decision.output.lightBulbState.decision_rules;
|
1587 |
|
1588 | // `minimalDecisionRules` has the mininum list of rules strictly equivalent to `decisionRules`
|
1589 | const minimalDecisionRules = craftai.interpreter.reduceDecisionRules(decisionRules)
|
1590 | ```
|
1591 |
|
1592 | ### Format decision rules ###
|
1593 |
|
1594 | From a list of decision rules, compute a _human readable_ version of these rules, in english.
|
1595 |
|
1596 | ```js
|
1597 | // `decision` is the decision tree as retrieved from taking a decision
|
1598 | const decision = craftai.interpreter.decide( ... );
|
1599 |
|
1600 | // `decisionRules` is the decision rules that led to decision for the `lightBulbState` value
|
1601 | const decisionRules = decision.output.lightBulbState.decision_rules;
|
1602 |
|
1603 | // `decisionRulesStr` is a human readable string representation of the rules.
|
1604 | const decisionRulesStr = craftai.interpreter.reduceDecisionRules.formatDecisionRules(decisionRules);
|
1605 | ```
|
1606 |
|
1607 | ### Get decision rules properties ###
|
1608 |
|
1609 | Retrieve the context properties that matters to a previously computed tree.
|
1610 |
|
1611 | ```js
|
1612 | // `tree` is the decision tree as retrieved through the craft ai REST API
|
1613 | const tree = { ... };
|
1614 |
|
1615 | const decisionRules = craftai.interpreter.getDecisionRulesProperties(tree)
|
1616 | ```
|
1617 |
|
1618 | Results for `craftai.interpreter.getDecisionRulesProperties` would look like:
|
1619 |
|
1620 | ```js
|
1621 | [
|
1622 | {
|
1623 | property: 'timeOfDay',
|
1624 | is_generated: true,
|
1625 | type: 'time_of_day'
|
1626 | }
|
1627 | ]
|
1628 | ```
|
1629 |
|
1630 | ## Logging ##
|
1631 |
|
1632 | The **craft ai** client is using [visionmedia/debug](https://www.npmjs.com/package/debug) under the namespace `'craft-ai:client:*'`, please refer to their documentation for further information.
|
1633 |
|