UNPKG

49.6 kBMarkdownView Raw
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
11If 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
15Once 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
17Once 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
25Let's first install the package from npm.
26
27```sh
28npm install craft-ai --save
29```
30Then import it in your code
31
32```js
33const craftai = require('craft-ai').createClient;
34```
35
36or using [es2015](https://babeljs.io/docs/learn-es2015/) syntax
37
38```js
39import craftai from 'craft-ai';
40```
41
42##### Plain Old Javascript #####
43
44Thanks 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
50to 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
60const 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
67An 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
69In 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
79const AGENT_ID = 'my_first_agent';
80
81client.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
109Pretty straightforward to test! Open [`https://beta.craft.ai/inspector`](https://beta.craft.ai/inspector), select you project and your agent is now listed.
110
111Now, 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
114const AGENT_ID = 'my_first_agent';
115
116client.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
133We 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
135Please note that only value changes are sent, thus if an operation doesn't contain a value, the previous known value is used.
136
137In the following we add 8 operations:
138
1391. 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;
1402. At 7:02, someone enters the room the light is turned on;
1413. At 7:15, someone else enters the room;
1424. At 7:31, the light is turned off;
1435. At 8:12, everyone leaves the room;
1446. At 19:23, 2 persons enter the room;
1457. At 22:35, the light is turned on;
1468. At 23:06, everyone leaves the room and the light is turned off.
147
148
149```js
150const AGENT_ID = 'my_first_agent';
151
152// for the purpose of this test, we delete and recreate the agent
153client.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
229In 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
235The 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
237The 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
240const AGENT_ID = 'my_first_agent';
241
242// for the purpose of this test, we delete and recreate the agent
243client.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
267Try 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
273Once 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
276const AGENT_ID = 'my_first_agent';
277
278// for the purpose of this test, we delete and recreate the agent
279client.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
313If 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
325Each 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
355If 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
357A 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
369And 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
392An `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
394A 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
409And 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
500Let'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
506The `time_quantum` is set to 100 seconds, which means that if the lightbulb
507color is changed from red to blue then from blue to purple in less that 1
508minutes and 40 seconds, only the change from red to purple will be taken into
509account.
510
511The `learning_period` is set to 108 000 seconds (one month) , which means that
512the state of the lightbulb from more than a month ago can be ignored when learning
513the 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
544In this second example, the `time` property is not generated, no property of
545type `timezone` is therefore needed. However values of `time` must be manually
546provided 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
577The `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
579From a unix timestamp and an explicit UTC offset:
580
581```js
582const 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
595From a unix timestamp and using the local UTC offset:
596
597```js
598const 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
612From a [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) string:
613
614```js
615const 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
628From a [moment](http://momentjs.com) (or [moment timezone](http://momentjs.com/timezone/)) instance:
629
630```js
631const 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
644Retrieve the current time with the local UTC offset:
645
646```js
647const now = new craftai.Time();
648```
649
650Retrieve the current time with a given UTC offset:
651
652```js
653const nowP5 = new craftai.Time(undefined, '+05:00');
654```
655
656### Advanced configuration
657
658The 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
664These 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
670Create 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
675client.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
731client.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
745client.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
759client.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
770Create and get a shareable url to view an agent tree.
771Only one url can be created at a time.
772
773```js
774client.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
788Delete a shareable url.
789The previous url cannot access the agent tree anymore.
790
791```js
792client.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
810client.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
877client.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
897client.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
912client.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
929Decision trees are computed at specific timestamps, directly by **craft ai** which learns from the context operations [added](#add-operations) throughout time.
930
931When 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
944client.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
1106The 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
1114To create several agents at once, use the method `createAgentBulk` as the following:
1115
1116```js
1117const agent_ID_1 = 'my_first_agent';
1118const agent_ID_2 = 'my_second_agent';
1119
1120const 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 };
1137const configuration_2 = { /* ... */ };
1138
1139const createBulkPayload = [
1140 {id: agent_ID_1, configuration: configuration_1},
1141 {id: agent_ID_2, configuration: configuration_2}
1142];
1143
1144client.createAgentBulk(createBulkPayload)
1145 .then(function(agents) {
1146 console.log(agents);
1147 })
1148 .catch(function(error) {
1149 console.error('Error!', error);
1150 })
1151```
1152
1153The 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
1174const agent_ID_1 = 'my_first_agent';
1175const agent_ID_2 = 'my_second_agent';
1176
1177const deleteBulkPayload = [
1178 { id: agent_ID_1 },
1179 { id: agent_ID_2 }
1180];
1181
1182client.deleteAgentBulk(deleteBulkPayload)
1183.then(function(deletedAgents) {
1184 console.log(agents);
1185})
1186.catch(function(error) {
1187 console.error('Error!', error);
1188});
1189```
1190The 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
1215const agent_ID_1 = 'my_first_agent';
1216const agent_ID_2 = 'my_second_agent';
1217
1218const 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 }];
1245const operations_agent_2 = [ /* ... */ ];
1246
1247const contextOperationBulkPayload = [
1248 { id: agent_ID_1, operations: operations_agent_1},
1249 { id: agent_ID_2, operations: operations_agent_2}
1250];
1251
1252client.addAgentContextOperationsBulk(contextOperationBulkPayload)
1253.then(function(agents) {
1254 console.log(agents);
1255})
1256.catch(function(error) {
1257 console.error('Error!', error);
1258});
1259```
1260
1261The 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
1278const agent_ID_1 = 'my_first_agent';
1279const agent_ID_2 = 'my_second_agent';
1280
1281const decisionTreePayload = [
1282 { id: agent_ID_1 },
1283 { id: agent_ID_2 }
1284];
1285
1286client.getAgentDecisionTreeBulk(decisionTreePayload)
1287.then(function(trees) {
1288 console.log(trees);
1289})
1290.catch(function(error) {
1291 console.error('Error!', error);
1292});
1293```
1294
1295The 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
1312The 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
1319const 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
1329It is possible to increase or decrease the timeout duration of `client.getAgentDecisionTree`, for exemple to account for especially long computations.
1330
1331```js
1332const 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
1344It 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
1347const 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
1356The 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
1362const tree = { ... };
1363// Compute the decision with specifying every context field
1364const 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`
1374const 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
1385A 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
1417A `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
1431A `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
1448A `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
1464From 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
1468const tree = { ... };
1469// Pass an array containing each context on which you want to take a decision
1470const 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
1489Results 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
1579From 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
1583const decision = craftai.interpreter.decide( ... );
1584
1585// `decisionRules` is the decision rules that led to decision for the `lightBulbState` value
1586const decisionRules = decision.output.lightBulbState.decision_rules;
1587
1588// `minimalDecisionRules` has the mininum list of rules strictly equivalent to `decisionRules`
1589const minimalDecisionRules = craftai.interpreter.reduceDecisionRules(decisionRules)
1590```
1591
1592### Format decision rules ###
1593
1594From 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
1598const decision = craftai.interpreter.decide( ... );
1599
1600// `decisionRules` is the decision rules that led to decision for the `lightBulbState` value
1601const decisionRules = decision.output.lightBulbState.decision_rules;
1602
1603// `decisionRulesStr` is a human readable string representation of the rules.
1604const decisionRulesStr = craftai.interpreter.reduceDecisionRules.formatDecisionRules(decisionRules);
1605```
1606
1607### Get decision rules properties ###
1608
1609Retrieve 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
1613const tree = { ... };
1614
1615const decisionRules = craftai.interpreter.getDecisionRulesProperties(tree)
1616```
1617
1618Results 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
1632The **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