├── .gitignore ├── LICENSE.md ├── README.md ├── data └── config-example.json ├── docs ├── animals.cypher ├── css │ └── bootstrap.css ├── img │ ├── LUIS-kg-apps.png │ ├── LUIS-kg-entities.png │ ├── LUIS-kg-entity-thing.png │ ├── LUIS-kg-entity-user.png │ ├── LUIS-kg-intent-launchDoYouLike.png │ ├── LUIS-kg-intent-launchUserLikes.png │ ├── LUIS-kg-intents.png │ ├── graph-editor-animals-IS_A.png │ ├── neo4j-kg-tutorial-LIKES-cynthia-no-penguins.png │ ├── neo4j-kg-tutorial-LIKES-cynthia-penguins.png │ ├── neo4j-kg-tutorial-animals-IS_A.png │ ├── neo4j-kg-tutorial-doYouLike-animals.png │ ├── neo4j-kg-tutorial-doYouLike-bats.png │ ├── neo4j-kg-tutorial-doYouLike-penguins-cynthia.png │ ├── neo4j-kg-tutorial-doYouLike-penguins.png │ ├── neo4j-kg-tutorial-doYouLike-whales.png │ ├── neo4j-kg-tutorial-mammals-LIKES.png │ ├── neo4j-kg-tutorial-neo4j-browser.png │ ├── neo4j-kg-tutorial-neo4j-desktop.png │ ├── neo4j-kg-tutorial-userLikes-cynthia.png │ └── neo4j-kg-tutorial-vscode.png ├── index.html ├── luis-knowledge-graph.json ├── luis-knowledge-graph.lu ├── neo4j-knowledge-graph-intro.html └── neo4j-knowledge-graph-intro.md ├── jest.config.js ├── package.json ├── src ├── DialogManager.ts ├── d3 │ └── d3Types.ts ├── index.ts ├── neo4j │ ├── Neo4jController.ts │ ├── helpers │ │ ├── BoltToD3.ts │ │ └── D3Helper.ts │ └── index.ts └── nlu │ ├── NLUController.ts │ ├── microsoft │ └── LUISController.ts │ └── node-nlp │ ├── NodeNlpController.test.ts │ └── NodeNlpController.ts ├── tools ├── test-dialog-manager.ts ├── test-luis-controller.ts ├── test-neo4j.ts └── test-node-nlp.ts ├── tsconfig.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | data/*config.json 4 | hide 5 | dist 6 | *.ipynb* 7 | .vscode 8 | hide 9 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018-2020 Andrew Rapo 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## neo4j-knowledge-graph 2 | 3 | [https://wwlib.github.io/neo4j-knowledge-graph/](https://wwlib.github.io/neo4j-knowledge-graph/) 4 | 5 | [https://github.com/wwlib/neo4j-knowledge-graph](https://github.com/wwlib/neo4j-knowledge-graph) 6 | 7 | [https://wwlib.github.io](https://wwlib.github.io/) 8 | 9 | An example of a simple, queryable knowledge graph implemented using neo4j with a node command line interface implemented in TypeScript. 10 | 11 | Note: Based on a python example created by Roberto Pieraccini (http://robertopieraccini.com/home/) 12 | 13 | Note: A nice tool for viewing and live-editing neo4j graphs is called Graph Editor and is available at http://wwlib.org/graph-editor/ 14 | 15 | ### Docs 16 | 17 | [https://wwlib.github.io/neo4j-knowledge-graph/neo4j-knowledge-graph-intro.html](https://wwlib.github.io/neo4j-knowledge-graph/neo4j-knowledge-graph-intro.html) 18 | 19 | ### Neo4j Setup 20 | 21 | Create an empty graph using the Neo4j Desktop app (https://neo4j.com/download-neo4j-now/). 22 | 23 | To create the *animals* graph, copy the contents of `docs/animals.cypher` into the neo4j browser cypher/query field. (http://localhost:7474/browser/) 24 | 25 | Verify the nodes/relationships with this cypher/query: `match (n) return n` 26 | 27 | ### NLU 28 | 29 | neo4j-knowledge-graph can be used with either node-nlp or Microsoft's LUIS NLU.com NLU. 30 | 31 | ### node-nlp 32 | 33 | By default, node-nlp is used to parse utterances (text input) for intents and entites. 34 | 35 | #### LUIS 36 | 37 | To use LUIS, create a LUIS app (https://www.luis.ai/) and configure it by importing `docs/luis-knowledge-graph.json`. 38 | 39 | ### Configuration 40 | 41 | Copy `data/config-example.json` to `data/config.json` and fill out the fields for Neo4j and LUIS (optional). 42 | 43 | ``` 44 | { 45 | "luis": { 46 | "endpoint": "", 47 | "appId": "", 48 | "subscriptionKey": "" 49 | }, 50 | "neo4j": { 51 | "url": "bolt://localhost:7687", 52 | "user": "neo4j", 53 | "password": "" 54 | } 55 | } 56 | ``` 57 | 58 | ### Installation and Running 59 | 60 | ``` 61 | yarn 62 | yarn start 63 | ``` 64 | 65 | #### Running 66 | 67 | `yarn start` 68 | or 69 | `yarn debug` 70 | 71 | #### CLI 72 | 73 | ? Ask a do-you-like question or say "[user] likes [something]". 74 | `do you like penguins` 75 | `do you like bats` 76 | `do you like mammals` 77 | 78 | #### Test Apps 79 | 80 | `yarn test-luis-controller --help` 81 | 82 | ``` 83 | Usage: yarn test-luis-controller [options] 84 | 85 | An application testing LUIS requests 86 | 87 | Options: 88 | 89 | -V, --version output the version number 90 | -q, --query The query to test 91 | -h, --help output usage information 92 | ``` 93 | 94 | `yarn test-neo4j --help` 95 | 96 | ``` 97 | Usage: yarn test-neo4j [options] 98 | 99 | An application testing for dialogflow requests 100 | 101 | Options: 102 | -V, --version output the version number 103 | -q, --query The query to test 104 | -c, --context 105 | -h, --help output usage information 106 | ``` 107 | 108 | `node ./dist/testNeo4jController.js --help` 109 | 110 | ``` 111 | Usage: testNeo4jController [options] 112 | 113 | An application for testing neo4j cyphers 114 | 115 | Options: 116 | 117 | -V, --version output the version number 118 | -c, --cypher Specify the cypher to test 119 | --d3 Parse results with D3Helper 120 | -h, --help output usage information 121 | ``` 122 | 123 | `yarn test-dialog-manager --help` 124 | 125 | ``` 126 | Usage: yarn test-dialog-manager [options] 127 | 128 | An application for testing the DialogManager class 129 | 130 | Options: 131 | 132 | -V, --version output the version number 133 | -q, --query The query to test 134 | -c, --context 135 | -n, --nlu luis, dialogflow, dialogflowV1 136 | -h, --help output usage information 137 | ``` 138 | -------------------------------------------------------------------------------- /data/config-example.json: -------------------------------------------------------------------------------- 1 | { 2 | "nluType": "node-nlp", 3 | "luis": { 4 | "endpoint": "https://westus.api.cognitive.microsoft.com/", 5 | "appId": "", 6 | "subscriptionKey": "" 7 | }, 8 | "neo4j": { 9 | "url": "bolt://localhost:7687", 10 | "user": "neo4j", 11 | "password": "" 12 | } 13 | } -------------------------------------------------------------------------------- /docs/animals.cypher: -------------------------------------------------------------------------------- 1 | CREATE 2 | (`43` :Animal {name:'Penguin',RobotLikes:'I like penguins, I kind of look like one'}) , 3 | (`6` :User {name:'Cynthia'}) , 4 | (`40` :Robot {name:'global'}) , 5 | (`46` :Animal {name:'Armadillo'}) , 6 | (`42` :Animal {name:'Zebra'}) , 7 | (`48` :Animal {name:'Fly',plural:'flies'}) , 8 | (`44` :Animal {name:'Flamingo'}) , 9 | (`41` :Animal {name:'Whale',RobotLikes:'You know it. One of my favorite mammals'}) , 10 | (`56` :User {name:'Andrew'}) , 11 | (`52` :AnimalType {name:'Mammal'}) , 12 | (`50` :AnimalType {name:'Bird',RobotLikes:'I like anything with wings'}) , 13 | (`45` :Animal {name:'Albatross',plural:'albatross'}) , 14 | (`47` :Animal {name:'Bat'}) , 15 | (`55` :AnimalType {name:'Insect'}) , 16 | (`49` :Animal {name:'Butterfly',plural:'butterflies'}) , 17 | (`51` :AnimalType {name:'Vertebrate'}) , 18 | (`53` :AnimalType {name:'Animal'}) , 19 | (`54` :AnimalType {name:'Invertebrate'}) , 20 | (`6`)-[:`LIKES` ]->(`40`), 21 | (`40`)-[:`LIKES` ]->(`46`), 22 | (`40`)-[:`LIKES` ]->(`42`), 23 | (`40`)-[:`LIKES` ]->(`48`), 24 | (`40`)-[:`LIKES` ]->(`44`), 25 | (`40`)-[:`LIKES` ]->(`41`), 26 | (`40`)-[:`LIKES` ]->(`43`), 27 | (`56`)-[:`LIKES` ]->(`41`), 28 | (`41`)-[:`IS_A` ]->(`52`), 29 | (`42`)-[:`IS_A` ]->(`52`), 30 | (`43`)-[:`IS_A` ]->(`50`), 31 | (`44`)-[:`IS_A` ]->(`50`), 32 | (`45`)-[:`IS_A` ]->(`50`), 33 | (`46`)-[:`IS_A` ]->(`52`), 34 | (`47`)-[:`IS_A` ]->(`52`), 35 | (`48`)-[:`IS_A` ]->(`55`), 36 | (`49`)-[:`IS_A` ]->(`55`), 37 | (`50`)-[:`IS_A` ]->(`51`), 38 | (`51`)-[:`IS_A` ]->(`53`), 39 | (`52`)-[:`IS_A` ]->(`51`), 40 | (`54`)-[:`IS_A` ]->(`53`), 41 | (`55`)-[:`IS_A` ]->(`54`) 42 | -------------------------------------------------------------------------------- /docs/img/LUIS-kg-apps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwlib/neo4j-knowledge-graph/1b9a855a823229227e814f8109eb410513d67365/docs/img/LUIS-kg-apps.png -------------------------------------------------------------------------------- /docs/img/LUIS-kg-entities.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwlib/neo4j-knowledge-graph/1b9a855a823229227e814f8109eb410513d67365/docs/img/LUIS-kg-entities.png -------------------------------------------------------------------------------- /docs/img/LUIS-kg-entity-thing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwlib/neo4j-knowledge-graph/1b9a855a823229227e814f8109eb410513d67365/docs/img/LUIS-kg-entity-thing.png -------------------------------------------------------------------------------- /docs/img/LUIS-kg-entity-user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwlib/neo4j-knowledge-graph/1b9a855a823229227e814f8109eb410513d67365/docs/img/LUIS-kg-entity-user.png -------------------------------------------------------------------------------- /docs/img/LUIS-kg-intent-launchDoYouLike.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwlib/neo4j-knowledge-graph/1b9a855a823229227e814f8109eb410513d67365/docs/img/LUIS-kg-intent-launchDoYouLike.png -------------------------------------------------------------------------------- /docs/img/LUIS-kg-intent-launchUserLikes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwlib/neo4j-knowledge-graph/1b9a855a823229227e814f8109eb410513d67365/docs/img/LUIS-kg-intent-launchUserLikes.png -------------------------------------------------------------------------------- /docs/img/LUIS-kg-intents.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwlib/neo4j-knowledge-graph/1b9a855a823229227e814f8109eb410513d67365/docs/img/LUIS-kg-intents.png -------------------------------------------------------------------------------- /docs/img/graph-editor-animals-IS_A.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwlib/neo4j-knowledge-graph/1b9a855a823229227e814f8109eb410513d67365/docs/img/graph-editor-animals-IS_A.png -------------------------------------------------------------------------------- /docs/img/neo4j-kg-tutorial-LIKES-cynthia-no-penguins.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwlib/neo4j-knowledge-graph/1b9a855a823229227e814f8109eb410513d67365/docs/img/neo4j-kg-tutorial-LIKES-cynthia-no-penguins.png -------------------------------------------------------------------------------- /docs/img/neo4j-kg-tutorial-LIKES-cynthia-penguins.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwlib/neo4j-knowledge-graph/1b9a855a823229227e814f8109eb410513d67365/docs/img/neo4j-kg-tutorial-LIKES-cynthia-penguins.png -------------------------------------------------------------------------------- /docs/img/neo4j-kg-tutorial-animals-IS_A.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwlib/neo4j-knowledge-graph/1b9a855a823229227e814f8109eb410513d67365/docs/img/neo4j-kg-tutorial-animals-IS_A.png -------------------------------------------------------------------------------- /docs/img/neo4j-kg-tutorial-doYouLike-animals.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwlib/neo4j-knowledge-graph/1b9a855a823229227e814f8109eb410513d67365/docs/img/neo4j-kg-tutorial-doYouLike-animals.png -------------------------------------------------------------------------------- /docs/img/neo4j-kg-tutorial-doYouLike-bats.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwlib/neo4j-knowledge-graph/1b9a855a823229227e814f8109eb410513d67365/docs/img/neo4j-kg-tutorial-doYouLike-bats.png -------------------------------------------------------------------------------- /docs/img/neo4j-kg-tutorial-doYouLike-penguins-cynthia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwlib/neo4j-knowledge-graph/1b9a855a823229227e814f8109eb410513d67365/docs/img/neo4j-kg-tutorial-doYouLike-penguins-cynthia.png -------------------------------------------------------------------------------- /docs/img/neo4j-kg-tutorial-doYouLike-penguins.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwlib/neo4j-knowledge-graph/1b9a855a823229227e814f8109eb410513d67365/docs/img/neo4j-kg-tutorial-doYouLike-penguins.png -------------------------------------------------------------------------------- /docs/img/neo4j-kg-tutorial-doYouLike-whales.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwlib/neo4j-knowledge-graph/1b9a855a823229227e814f8109eb410513d67365/docs/img/neo4j-kg-tutorial-doYouLike-whales.png -------------------------------------------------------------------------------- /docs/img/neo4j-kg-tutorial-mammals-LIKES.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwlib/neo4j-knowledge-graph/1b9a855a823229227e814f8109eb410513d67365/docs/img/neo4j-kg-tutorial-mammals-LIKES.png -------------------------------------------------------------------------------- /docs/img/neo4j-kg-tutorial-neo4j-browser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwlib/neo4j-knowledge-graph/1b9a855a823229227e814f8109eb410513d67365/docs/img/neo4j-kg-tutorial-neo4j-browser.png -------------------------------------------------------------------------------- /docs/img/neo4j-kg-tutorial-neo4j-desktop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwlib/neo4j-knowledge-graph/1b9a855a823229227e814f8109eb410513d67365/docs/img/neo4j-kg-tutorial-neo4j-desktop.png -------------------------------------------------------------------------------- /docs/img/neo4j-kg-tutorial-userLikes-cynthia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwlib/neo4j-knowledge-graph/1b9a855a823229227e814f8109eb410513d67365/docs/img/neo4j-kg-tutorial-userLikes-cynthia.png -------------------------------------------------------------------------------- /docs/img/neo4j-kg-tutorial-vscode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwlib/neo4j-knowledge-graph/1b9a855a823229227e814f8109eb410513d67365/docs/img/neo4j-kg-tutorial-vscode.png -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | neo4j-knowledge-graph 4 | 5 | 17 | 18 | 19 | 20 |

neo4j-knowledge-graph

21 | 22 |

https://wwlib.github.io/neo4j-knowledge-graph/

23 | 24 |

https://github.com/wwlib/neo4j-knowledge-graph

25 | 26 |

https://wwlib.github.io

27 | 28 |

An example of a simple, queryable knowledge graph implemented using neo4j with a node command line interface implemented in TypeScript.

29 | 30 |

Note: Based on a python example created by Roberto Pieraccini (http://robertopieraccini.com/home/)

31 | 32 |

Note: A nice tool for viewing and live-editing neo4j graphs is called Graph Editor and is available at http://wwlib.org/graph-editor/

33 | 34 |

Docs

35 | 36 |

https://wwlib.github.io/neo4j-knowledge-graph/neo4j-knowledge-graph-intro.html

37 | 38 |

Neo4j Setup

39 | 40 |

Create an empty graph using the Neo4j Desktop app (https://neo4j.com/download-neo4j-now/).

41 | 42 |

To create the animals graph, copy the contents of docs/animals.cypher into the neo4j browser cypher/query field. (http://localhost:7474/browser/)

43 | 44 |

Verify the nodes/relationships with this cypher/query: match (n) return n

45 | 46 |

NLU

47 | 48 |

neo4j-knowledge-graph can be used with either node-nlp or Microsoft's LUIS NLU.

49 | 50 |

node-nlp

51 | 52 |

By default, node-nlp is used to parse utterances (text input) for intents and entites.

53 | 54 |

LUIS

55 | 56 |

To use LUIS, create a LUIS app (https://www.luis.ai/) and configure it by importing docs/luis-knowledge-graph.json.

57 | 58 | 59 |

Configuration

60 | 61 |

Copy data/config-example.json to data/config.json and fill out the fields for Neo4j and LUIS (optional).

62 | 63 |
{
 64 |    "luis": {
 65 |         "endpoint": "",
 66 |         "appId": "",
 67 |         "subscriptionKey": ""
 68 |     },
 69 |     "neo4j": {
 70 |         "url": "bolt://localhost:7687",
 71 |         "user": "neo4j",
 72 |         "password": ""
 73 |     }
 74 | }
 75 | 
76 | 77 |

Installation and Running

78 | 79 |

 80 | yarn
 81 | yarn start
 82 | 
83 | 84 |

Running

85 | 86 |

yarn start
87 | or
88 | yarn debug

89 | 90 |

CLI

91 | 92 |

? Ask a do-you-like question or say "[user] likes [something]".
93 | do you like penguins
94 | do you like bats
95 | do you like mammals

96 | 97 |

Test Apps

98 | 99 |

yarn test-luis-controller --help

100 | 101 |
Usage: yarn test-luis-controller [options]
102 | 
103 | An application testing LUIS requests
104 | 
105 | Options:
106 | 
107 |   -V, --version            output the version number
108 |   -q, --query <query>      The query to test
109 |   -h, --help               output usage information
110 | 
111 | 112 | 113 |

yarn test-neo4j --help

114 | 115 |
Usage: yarn test-neo4j [options]
116 | 
117 | An application for testing neo4j cyphers
118 | 
119 | Options:
120 | 
121 |   -V, --version          output the version number
122 |   -c, --cypher <cypher>  Specify the cypher to test
123 |   --d3                   Parse results with D3Helper
124 |   -h, --help             output usage information
125 | 
126 | 127 |

yarn test-dialog-manager --help

128 | 129 |
Usage: yarn test-dialog-manager [options]
130 | 
131 | An application for testing the DialogManager class
132 | 
133 | Options:
134 | 
135 |   -V, --version            output the version number
136 |   -q, --query <query>      The query to test
137 |   -c, --context <context>
138 |   -n, --nlu <nlu>          luis, node-nlp (default)
139 |   -h, --help               output usage information
140 | 
141 | 142 | 143 | 144 | -------------------------------------------------------------------------------- /docs/luis-knowledge-graph.json: -------------------------------------------------------------------------------- 1 | { 2 | "luis_schema_version": "7.0.0", 3 | "intents": [ 4 | { 5 | "name": "executeGameCommand", 6 | "features": [] 7 | }, 8 | { 9 | "name": "launchClock", 10 | "features": [] 11 | }, 12 | { 13 | "name": "launchDoYouLike", 14 | "features": [] 15 | }, 16 | { 17 | "name": "launchGame", 18 | "features": [] 19 | }, 20 | { 21 | "name": "launchJoke", 22 | "features": [] 23 | }, 24 | { 25 | "name": "launchUserLikes", 26 | "features": [] 27 | }, 28 | { 29 | "name": "None", 30 | "features": [] 31 | } 32 | ], 33 | "entities": [], 34 | "hierarchicals": [], 35 | "composites": [], 36 | "closedLists": [ 37 | { 38 | "name": "gameCommand", 39 | "subLists": [ 40 | { 41 | "canonicalForm": "continue", 42 | "list": [ 43 | "go" 44 | ] 45 | }, 46 | { 47 | "canonicalForm": "quit", 48 | "list": [] 49 | }, 50 | { 51 | "canonicalForm": "equip", 52 | "list": [] 53 | }, 54 | { 55 | "canonicalForm": "fight", 56 | "list": [] 57 | }, 58 | { 59 | "canonicalForm": "run", 60 | "list": [] 61 | }, 62 | { 63 | "canonicalForm": "north", 64 | "list": [] 65 | }, 66 | { 67 | "canonicalForm": "south", 68 | "list": [] 69 | }, 70 | { 71 | "canonicalForm": "east", 72 | "list": [ 73 | "exit" 74 | ] 75 | }, 76 | { 77 | "canonicalForm": "west", 78 | "list": [] 79 | }, 80 | { 81 | "canonicalForm": "close", 82 | "list": [] 83 | }, 84 | { 85 | "canonicalForm": "enter", 86 | "list": [] 87 | }, 88 | { 89 | "canonicalForm": "open", 90 | "list": [] 91 | } 92 | ], 93 | "roles": [ 94 | "command" 95 | ] 96 | }, 97 | { 98 | "name": "gameObject", 99 | "subLists": [ 100 | { 101 | "canonicalForm": "sword", 102 | "list": [] 103 | }, 104 | { 105 | "canonicalForm": "door", 106 | "list": [] 107 | }, 108 | { 109 | "canonicalForm": "chest", 110 | "list": [] 111 | } 112 | ], 113 | "roles": [ 114 | "object" 115 | ] 116 | }, 117 | { 118 | "name": "thing", 119 | "subLists": [ 120 | { 121 | "canonicalForm": "Whale", 122 | "list": [ 123 | "whale", 124 | "whales" 125 | ] 126 | }, 127 | { 128 | "canonicalForm": "Insect", 129 | "list": [ 130 | "insect,", 131 | "insects" 132 | ] 133 | }, 134 | { 135 | "canonicalForm": "Chicken", 136 | "list": [ 137 | "chicken,", 138 | "chickens" 139 | ] 140 | }, 141 | { 142 | "canonicalForm": "Flamingo", 143 | "list": [ 144 | "flamingo", 145 | "flamingos" 146 | ] 147 | }, 148 | { 149 | "canonicalForm": "Animal", 150 | "list": [ 151 | "animal", 152 | "animals" 153 | ] 154 | }, 155 | { 156 | "canonicalForm": "Armadillo", 157 | "list": [ 158 | "armadillo", 159 | "armadillos" 160 | ] 161 | }, 162 | { 163 | "canonicalForm": "Bat", 164 | "list": [ 165 | "bat", 166 | "bats" 167 | ] 168 | }, 169 | { 170 | "canonicalForm": "Penguin", 171 | "list": [ 172 | "penguin", 173 | "penguins" 174 | ] 175 | }, 176 | { 177 | "canonicalForm": "Bird", 178 | "list": [ 179 | "bird", 180 | "birds" 181 | ] 182 | }, 183 | { 184 | "canonicalForm": "Breakfast", 185 | "list": [ 186 | "breakfast,", 187 | "breakfasts" 188 | ] 189 | }, 190 | { 191 | "canonicalForm": "Monkey", 192 | "list": [ 193 | "monkey", 194 | "monkeys" 195 | ] 196 | }, 197 | { 198 | "canonicalForm": "Zebra", 199 | "list": [ 200 | "zebra", 201 | "zebras" 202 | ] 203 | }, 204 | { 205 | "canonicalForm": "Albatross", 206 | "list": [ 207 | "albatross", 208 | "albatrosses" 209 | ] 210 | }, 211 | { 212 | "canonicalForm": "Mammal", 213 | "list": [ 214 | "mammal", 215 | "mammals" 216 | ] 217 | }, 218 | { 219 | "canonicalForm": "Vertebrate", 220 | "list": [ 221 | "vertebrate", 222 | "vertebrates" 223 | ] 224 | }, 225 | { 226 | "canonicalForm": "Cheese", 227 | "list": [ 228 | "cheese", 229 | "cheeses" 230 | ] 231 | }, 232 | { 233 | "canonicalForm": "Butterfly", 234 | "list": [ 235 | "butterfly", 236 | "butterflies" 237 | ] 238 | }, 239 | { 240 | "canonicalForm": "Invertebrate", 241 | "list": [ 242 | "invertebrate", 243 | "invertebrates" 244 | ] 245 | } 246 | ], 247 | "roles": [] 248 | }, 249 | { 250 | "name": "user", 251 | "subLists": [ 252 | { 253 | "canonicalForm": "Andrew", 254 | "list": [ 255 | "andy" 256 | ] 257 | }, 258 | { 259 | "canonicalForm": "Cynthia", 260 | "list": [] 261 | }, 262 | { 263 | "canonicalForm": "Robert", 264 | "list": [ 265 | "bob" 266 | ] 267 | }, 268 | { 269 | "canonicalForm": "Eric", 270 | "list": [ 271 | "rick" 272 | ] 273 | }, 274 | { 275 | "canonicalForm": "Jane", 276 | "list": [] 277 | } 278 | ], 279 | "roles": [] 280 | } 281 | ], 282 | "prebuiltEntities": [], 283 | "utterances": [ 284 | { 285 | "text": "andrew likes cheese", 286 | "intent": "launchUserLikes", 287 | "entities": [] 288 | }, 289 | { 290 | "text": "andrew really likes whales", 291 | "intent": "launchUserLikes", 292 | "entities": [] 293 | }, 294 | { 295 | "text": "are you a fan of whales?", 296 | "intent": "launchDoYouLike", 297 | "entities": [] 298 | }, 299 | { 300 | "text": "check the time", 301 | "intent": "launchClock", 302 | "entities": [] 303 | }, 304 | { 305 | "text": "do you know any jokes", 306 | "intent": "launchJoke", 307 | "entities": [] 308 | }, 309 | { 310 | "text": "do you like animals", 311 | "intent": "launchDoYouLike", 312 | "entities": [] 313 | }, 314 | { 315 | "text": "do you like cheese?", 316 | "intent": "launchDoYouLike", 317 | "entities": [] 318 | }, 319 | { 320 | "text": "do you love armadillos", 321 | "intent": "launchDoYouLike", 322 | "entities": [] 323 | }, 324 | { 325 | "text": "east", 326 | "intent": "executeGameCommand", 327 | "entities": [] 328 | }, 329 | { 330 | "text": "i would like to continue", 331 | "intent": "executeGameCommand", 332 | "entities": [] 333 | }, 334 | { 335 | "text": "i would like to equip the sword", 336 | "intent": "executeGameCommand", 337 | "entities": [] 338 | }, 339 | { 340 | "text": "jeri loves penguins", 341 | "intent": "launchUserLikes", 342 | "entities": [] 343 | }, 344 | { 345 | "text": "let's equip my sword", 346 | "intent": "executeGameCommand", 347 | "entities": [] 348 | }, 349 | { 350 | "text": "let's exit", 351 | "intent": "executeGameCommand", 352 | "entities": [] 353 | }, 354 | { 355 | "text": "let's open the chest", 356 | "intent": "executeGameCommand", 357 | "entities": [] 358 | }, 359 | { 360 | "text": "let's play a game", 361 | "intent": "launchGame", 362 | "entities": [] 363 | }, 364 | { 365 | "text": "let's play the game", 366 | "intent": "launchGame", 367 | "entities": [] 368 | }, 369 | { 370 | "text": "play a game", 371 | "intent": "launchGame", 372 | "entities": [] 373 | }, 374 | { 375 | "text": "play the game", 376 | "intent": "launchGame", 377 | "entities": [] 378 | }, 379 | { 380 | "text": "say something funny", 381 | "intent": "launchJoke", 382 | "entities": [] 383 | }, 384 | { 385 | "text": "tell me a joke", 386 | "intent": "launchJoke", 387 | "entities": [] 388 | }, 389 | { 390 | "text": "tell me something funny", 391 | "intent": "launchJoke", 392 | "entities": [] 393 | }, 394 | { 395 | "text": "tell me the time", 396 | "intent": "launchClock", 397 | "entities": [] 398 | }, 399 | { 400 | "text": "tell me what time it is", 401 | "intent": "launchClock", 402 | "entities": [] 403 | }, 404 | { 405 | "text": "tell us a joke", 406 | "intent": "launchJoke", 407 | "entities": [] 408 | }, 409 | { 410 | "text": "what time is it", 411 | "intent": "launchClock", 412 | "entities": [] 413 | }, 414 | { 415 | "text": "what's the time", 416 | "intent": "launchClock", 417 | "entities": [] 418 | } 419 | ], 420 | "versionId": "0.3", 421 | "name": "neo4j-kg", 422 | "desc": "For use with a neo4j knowledge graph prototype.", 423 | "culture": "en-us", 424 | "tokenizerVersion": "1.0.0", 425 | "patternAnyEntities": [], 426 | "regex_entities": [], 427 | "phraselists": [], 428 | "regex_features": [], 429 | "patterns": [], 430 | "settings": [] 431 | } -------------------------------------------------------------------------------- /docs/luis-knowledge-graph.lu: -------------------------------------------------------------------------------- 1 | 2 | > LUIS application information 3 | > !# @app.name = neo4j-kg 4 | > !# @app.desc = For use with a neo4j knowledge graph prototype. 5 | > !# @app.versionId = 0.3 6 | > !# @app.culture = en-us 7 | > !# @app.luis_schema_version = 7.0.0 8 | > !# @app.tokenizerVersion = 1.0.0 9 | 10 | 11 | > # Intent definitions 12 | 13 | ## executeGameCommand 14 | - east 15 | - i would like to continue 16 | - i would like to equip the sword 17 | - let's equip my sword 18 | - let's exit 19 | - let's open the chest 20 | 21 | 22 | ## launchClock 23 | - check the time 24 | - tell me the time 25 | - tell me what time it is 26 | - what time is it 27 | - what's the time 28 | 29 | 30 | ## launchDoYouLike 31 | - are you a fan of whales? 32 | - do you like animals 33 | - do you like cheese? 34 | - do you love armadillos 35 | 36 | 37 | ## launchGame 38 | - let's play a game 39 | - let's play the game 40 | - play a game 41 | - play the game 42 | 43 | 44 | ## launchJoke 45 | - do you know any jokes 46 | - say something funny 47 | - tell me a joke 48 | - tell me something funny 49 | - tell us a joke 50 | 51 | 52 | ## launchUserLikes 53 | - andrew likes cheese 54 | - andrew really likes whales 55 | - jeri loves penguins 56 | 57 | 58 | ## None 59 | 60 | 61 | > # Entity definitions 62 | 63 | 64 | > # PREBUILT Entity definitions 65 | 66 | 67 | > # Phrase list definitions 68 | 69 | 70 | > # List entities 71 | 72 | @ list gameCommand hasRole command = 73 | - continue : 74 | - go 75 | - quit : 76 | - equip : 77 | - fight : 78 | - run : 79 | - north : 80 | - south : 81 | - east : 82 | - exit 83 | - west : 84 | - close : 85 | - enter : 86 | - open : 87 | 88 | 89 | @ list gameObject hasRole object = 90 | - sword : 91 | - door : 92 | - chest : 93 | 94 | 95 | @ list thing = 96 | - Whale : 97 | - whale 98 | - whales 99 | - Insect : 100 | - insect, 101 | - insects 102 | - Chicken : 103 | - chicken, 104 | - chickens 105 | - Flamingo : 106 | - flamingo 107 | - flamingos 108 | - Animal : 109 | - animal 110 | - animals 111 | - Armadillo : 112 | - armadillo 113 | - armadillos 114 | - Bat : 115 | - bat 116 | - bats 117 | - Penguin : 118 | - penguin 119 | - penguins 120 | - Bird : 121 | - bird 122 | - birds 123 | - Breakfast : 124 | - breakfast, 125 | - breakfasts 126 | - Monkey : 127 | - monkey 128 | - monkeys 129 | - Zebra : 130 | - zebra 131 | - zebras 132 | - Albatross : 133 | - albatross 134 | - albatrosses 135 | - Mammal : 136 | - mammal 137 | - mammals 138 | - Vertebrate : 139 | - vertebrate 140 | - vertebrates 141 | - Cheese : 142 | - cheese 143 | - cheeses 144 | - Butterfly : 145 | - butterfly 146 | - butterflies 147 | - Invertebrate : 148 | - invertebrate 149 | - invertebrates 150 | 151 | 152 | @ list user = 153 | - Andrew : 154 | - andy 155 | - Cynthia : 156 | - Robert : 157 | - bob 158 | - Eric : 159 | - rick 160 | - Jane : 161 | 162 | 163 | > # RegEx entities 164 | 165 | 166 | -------------------------------------------------------------------------------- /docs/neo4j-knowledge-graph-intro.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | neo4j-knowledge-graph - Intro 4 | 5 | 17 | 18 | 19 | 20 |

neo4j-knowledge-graph

21 | 22 |

Subject: Neo4j Knowledge Graph
23 | Language: TypeScript (node)
24 | Repo: git@github.com:wwlib/neo4j-knowledge-graph.git
25 | Related: Electron-based Graph Editor Tool: https://wwlib.github.io/graph-editor/

26 | 27 |

https://wwlib.github.io/neo4j-knowledge-graph/

28 | 29 |

https://github.com/wwlib/neo4j-knowledge-graph

30 | 31 |

https://wwlib.github.io

32 | 33 |

An example of a simple, queryable knowledge graph implemented using neo4j with a node command line interface implemented in TypeScript.

34 | 35 |

Knowledge Graph Demo Demo Running on Jibo (YouTube): 36 | 37 |

38 | 39 | 40 | 41 |

Note: The code described in this post is based on an example that I worked on with Roberto Pieraccini (http://robertopieraccini.com/home/) at Jibo, Inc. We used a similar example to test a knowledge-graph-enhanced dialog running on Jibo (as seen in the video referenced above)

42 |

Note: A nice tool for viewing and live-editing neo4j graphs is called Graph Editor and is available at http://wwlib.org/graph-editor/

43 |

Overview

44 |

Overview video (YouTube): https://www.youtube.com/embed/YFRiWiZJPkU

45 |

Getting Started

46 |

One way to enhance NLU-driven dialog interactions is to make use of a knowledge graph. This example uses a neo4j graph (database) to store and query a knowledge graph (kg) containing information about animals. This simple example kg represents the relationships between a robot, and handful of animals, a few animal types and a couple of humans.

47 |

This command-line version uses node-nlp by default and can be configured to use Microsoft's LUIS NLU. A neo4j graph database is also required (ideally an empty graph). Some setup is required to get started.

48 |

Setup

49 |

1 - Clone and install the neo4j-knowledge-graph repo

50 |
git clone git@github.com:wwlib/neo4j-knowledge-graph.git
 51 | cd  neo4j-knowledge-graph
 52 | yarn
 53 | 

The project looks like this:

54 |

neo4j-knowledge-graph

55 |

2 - NLU 56 | (optional: create a LUIS NLU agent by uploading the included agent description file to your LUIS account:

57 |
    58 |
  • docs/luis-knowledge-graph.lu
  • 59 |
60 |

See the NLU screenshots at the end of this post for descriptions of the LUIS agent.

61 |

3 - Neo4j 62 | Download and install the free Neo4j Desktop app from https://neo4j.com/download-neo4j-now/ and create a new graph.

63 |

neo4j-knowledge-graph

64 |

Start the graph and access it in the browser at: http://localhost:7474/browser/

65 |

To populate the graph, paste the contents of docs/animals.cypher into the neo4j browser query field. Then verify that the graph is ready by entering this cypher into the browser query field:

66 |

MATCH (n) return n limit 100

67 |

The result should look like the image below.

68 |

neo4j-knowledge-graph

69 |

To see just the animal relationships, enter this cypher into the browser query field:

70 |

match (n)-[r:IS_A]-(m) return n, r, m

71 |

The result should look like the image below

72 |

neo4j-knowledge-graph

73 |

4 - Configuring the neo4j-knowledge-graph command-line app

74 |

Copy data/config-example.json and rename it data/config.json

75 |

Fill out the user and password fields for your neo4j graph. (optional: fill out the access credentials for your LUIS agent).

76 |
{
 77 |    "luis": {
 78 |         "endpoint": "",
 79 |         "appId": "",
 80 |         "subscriptionKey": ""
 81 |     },
 82 |     "neo4j": {
 83 |         "url": "bolt://localhost:7687",
 84 |         "user": "neo4j",
 85 |         "password": ""
 86 |     }
 87 | }
 88 | 

Save the data/config.json file.

89 |

Running the command line app

90 |

In a terminal window, in the neo4j-knowledge-graph directory, build and run the command line app:

91 |
yarn build
 92 | yarn debug
 93 | 

If everything is setup correctly, you should should be prompted with:

94 |

Ask a do-you-like question or say “[user] likes [something]”

95 |

Try asking (typing): do you like penguins

96 |

The response should look like the screen below:

97 |

neo4j-knowledge-graph

98 |

The example graph contains a set of animal-related nodes and relationships that looks like this (as seen in Graph Editor):

99 |

neo4j-knowledge-graph

100 |

The graph also contains nodes and relationship relating a robot and a couple of users to the animals:

101 |

neo4j-knowledge-graph

102 |

According to the graph, the robot likes Flies, Zebras, Whales, Armadillos, Flamingos, and Penguins. Andrew likes whales. Cynthia likes the robot. Asking, “do you like whales” produces the response below: “You know it. One of my favorite animals...and I know that Andrew likes them too.”

103 |

Note: In the Penguin node has a special RobotLikes property which is used in the response. A generic response is generated for nodes that do not have this property.

104 |

neo4j-knowledge-graph

105 |

To answer the question, “Do you like bats”, the app must make an upward reference because the robot does not have a direct LIKES relationship to bats. The app checks the bat’s parent node, AnimalType (Mammal), and then responds with a list of mammals that the robot does like:

106 |

“I don’t know if I like bats, but they are Mammals and I do like Mammals. Of all the Mammals I like Armadillos, Zebras, and Whales.”

107 |

neo4j-knowledge-graph

108 |

neo4j-knowledge-graph

109 |

The graph can be modified by asserting that a user likes something. For example, asserting that “cynthia likes penguins” produces the result:

110 |

OK. I understand that cynthia likes penguins. That’s cool.

111 |

neo4j-knowledge-graph

112 |

Querying the graph now reveals that the fact that Cynthia likes penguins has been incorporated:

113 |

match (n)-[r:LIKES]-(m) return n, r, m

114 |

neo4j-knowledge-graph

115 |

Now, asking if the robot likes penguins produces a response that includes the new information about Cynthia:

116 |

neo4j-knowledge-graph

117 |

When asking about an AnimalType, like ‘Animal’, the answer will include all of the descendants that are liked by the robot.

118 |

neo4j-knowledge-graph

119 |

Summary

120 |

This simple example offers a quick way to incorporate a knowledge graph into a dialog interaction. As seen in the intro video (at the start of the post) when this kind of interaction is embodied by a social robot like Jibo, the experience is compelling - especially when the app/robot remembers information asserted by the user.

121 |

The example suggests that knowledge graphs may offer a path to take to making automated dialog significantly more human-like

122 |

Appendix: NLU Setup Details

123 |

LUIS

124 |

LUIS

125 |

LUIS

126 |

LUIS

127 |

LUIS

128 |

LUIS

129 |

LUIS

130 |

LUIS

131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /docs/neo4j-knowledge-graph-intro.md: -------------------------------------------------------------------------------- 1 | ## neo4j-knowledge-graph 2 | 3 | *Subject*: Neo4j Knowledge Graph 4 | *Language*: TypeScript (node) 5 | *Repo*: git@github.com:wwlib/neo4j-knowledge-graph.git 6 | *Related*: Electron-based Graph Editor Tool: [https://wwlib.github.io/graph-editor/](https://wwlib.github.io/graph-editor/) 7 | 8 | [https://wwlib.github.io/neo4j-knowledge-graph/](https://wwlib.github.io/neo4j-knowledge-graph/) 9 | 10 | [https://github.com/wwlib/neo4j-knowledge-graph](https://github.com/wwlib/neo4j-knowledge-graph) 11 | 12 | [https://wwlib.github.io](https://wwlib.github.io/) 13 | 14 | An example of a simple, queryable knowledge graph implemented using neo4j with a node command line interface implemented in TypeScript. 15 | 16 | Knowledge Graph Demo Demo Running on Jibo (YouTube): [https://www.youtube.com/embed/0oVCR3pIz0Q](https://www.youtube.com/embed/0oVCR3pIz0Q) 17 | 18 | Note: The code described in this post is based on an example that I worked on with Roberto Pieraccini ([http://robertopieraccini.com/home/](http://robertopieraccini.com/home/)) at Jibo, Inc. We used a similar example to test a knowledge-graph-enhanced dialog running on Jibo (as seen in the video referenced above) 19 | 20 | Note: A nice tool for viewing and live-editing neo4j graphs is called Graph Editor and is available at [http://wwlib.org/graph-editor/](http://wwlib.org/graph-editor/) 21 | 22 | ### Overview 23 | 24 | Overview video (YouTube): [https://www.youtube.com/embed/YFRiWiZJPkU](https://www.youtube.com/embed/pEps_xaUWVo) 25 | 26 | ### Getting Started 27 | 28 | One way to enhance NLU-driven dialog interactions is to make use of a knowledge graph. This example uses a neo4j graph (database) to store and query a knowledge graph (kg) containing information about animals. This simple example kg represents the relationships between a robot, and handful of animals, a few animal types and a couple of humans. 29 | 30 | This command-line version uses node-nlp by default and can be configured to use Microsoft's LUIS NLU. A neo4j graph database is also required (ideally an empty graph). Some setup is required to get started. 31 | 32 | 33 | #### Setup 34 | 35 | 1 - Clone and install the neo4j-knowledge-graph repo 36 | ``` 37 | git clone git@github.com:wwlib/neo4j-knowledge-graph.git 38 | cd neo4j-knowledge-graph 39 | yarn 40 | ``` 41 | 42 | The project looks like this: 43 | 44 | ![neo4j-knowledge-graph](./img/neo4j-kg-tutorial-vscode.png) 45 | 46 | 2 - NLU 47 | (optional: create either a LUIS agent by uploading the included agent description files to your LUIS account: 48 | 49 | - docs/luis-knowledge-graph.lu 50 | 51 | See the NLU screenshots at the end of this post for descriptions of the LUIS agent. 52 | 53 | 3 - Neo4j 54 | Download and install the free Neo4j Desktop app from [https://neo4j.com/download-neo4j-now/](https://neo4j.com/download-neo4j-now/) and create a new graph. 55 | 56 | ![neo4j-knowledge-graph](./img/neo4j-kg-tutorial-neo4j-desktop.png) 57 | 58 | Start the graph and access it in the browser at: [http://localhost:7474/browser/](http://localhost:7474/browser/) 59 | 60 | To populate the graph, paste the contents of docs/animals.cypher into the neo4j browser query field. Then verify that the graph is ready by entering this cypher into the browser query field: 61 | 62 | `MATCH (n) return n limit 100` 63 | 64 | The result should look like the image below. 65 | 66 | ![neo4j-knowledge-graph](./img/neo4j-kg-tutorial-neo4j-browser.png) 67 | 68 | To see just the animal relationships, enter this cypher into the browser query field: 69 | 70 | `match (n)-[r:IS_A]-(m) return n, r, m` 71 | 72 | The result should look like the image below 73 | 74 | ![neo4j-knowledge-graph](./img/neo4j-kg-tutorial-animals-IS_A.png) 75 | 76 | 4 - Configuring the neo4j-knowledge-graph command-line app 77 | 78 | Copy `data/config-example.json` and rename it `data/config.json` 79 | 80 | Fill out the user and password fields for your neo4j graph. (optional: fill out the access credentials for your LUIS agent). 81 | 82 | ``` 83 | { 84 | "luis": { 85 | "endpoint": "", 86 | "appId": "", 87 | "subscriptionKey": "" 88 | }, 89 | "neo4j": { 90 | "url": "bolt://localhost:7687", 91 | "user": "neo4j", 92 | "password": "" 93 | } 94 | } 95 | ``` 96 | 97 | Save the `data/config.json` file. 98 | 99 | #### Running the command line app 100 | 101 | In a terminal window, in the `neo4j-knowledge-graph` directory, build and run the command line app: 102 | 103 | ``` 104 | yarn build 105 | yarn debug 106 | ``` 107 | 108 | If everything is setup correctly, you should should be prompted with: 109 | 110 | `Ask a do-you-like question or say “[user] likes [something]”` 111 | 112 | Try asking (typing): `do you like penguins` 113 | 114 | The response should look like the screen below: 115 | 116 | ![neo4j-knowledge-graph](./img/neo4j-kg-tutorial-doYouLike-penguins.png) 117 | 118 | The example graph contains a set of animal-related nodes and relationships that looks like this (as seen in Graph Editor): 119 | 120 | ![neo4j-knowledge-graph](./img/graph-editor-animals-IS_A.png) 121 | 122 | The graph also contains nodes and relationship relating a robot and a couple of users to the animals: 123 | 124 | ![neo4j-knowledge-graph](./img/neo4j-kg-tutorial-LIKES-cynthia-no-penguins.png) 125 | 126 | According to the graph, the robot likes Flies, Zebras, Whales, Armadillos, Flamingos, and Penguins. Andrew likes whales. Cynthia likes the robot. Asking, “do you like whales” produces the response below: “You know it. One of my favorite animals...and I know that Andrew likes them too.” 127 | 128 | Note: In the Penguin node has a special RobotLikes property which is used in the response. A generic response is generated for nodes that do not have this property. 129 | 130 | ![neo4j-knowledge-graph](./img/neo4j-kg-tutorial-doYouLike-whales.png) 131 | 132 | To answer the question, “Do you like bats”, the app must make an upward reference because the robot does not have a direct LIKES relationship to bats. The app checks the bat’s parent node, AnimalType (Mammal), and then responds with a list of mammals that the robot does like: 133 | 134 | “I don’t know if I like bats, but they are Mammals and I do like Mammals. Of all the Mammals I like Armadillos, Zebras, and Whales.” 135 | 136 | ![neo4j-knowledge-graph](./img/neo4j-kg-tutorial-doYouLike-bats.png) 137 | 138 | ![neo4j-knowledge-graph](./img/neo4j-kg-tutorial-mammals-LIKES.png) 139 | 140 | The graph can be modified by asserting that a user likes something. For example, asserting that “cynthia likes penguins” produces the result: 141 | 142 | OK. I understand that cynthia likes penguins. That’s cool. 143 | 144 | 145 | ![neo4j-knowledge-graph](./img/neo4j-kg-tutorial-userLikes-cynthia.png) 146 | 147 | Querying the graph now reveals that the fact that Cynthia likes penguins has been incorporated: 148 | 149 | `match (n)-[r:LIKES]-(m) return n, r, m` 150 | 151 | ![neo4j-knowledge-graph](./img/neo4j-kg-tutorial-LIKES-cynthia-penguins.png) 152 | 153 | Now, asking if the robot likes penguins produces a response that includes the new information about Cynthia: 154 | 155 | ![neo4j-knowledge-graph](./img/neo4j-kg-tutorial-doYouLike-penguins-cynthia.png) 156 | 157 | When asking about an AnimalType, like ‘Animal’, the answer will include all of the descendants that are liked by the robot. 158 | 159 | ![neo4j-knowledge-graph](./img/neo4j-kg-tutorial-doYouLike-animals.png) 160 | 161 | ### Summary 162 | 163 | This simple example offers a quick way to incorporate a knowledge graph into a dialog interaction. As seen in the intro video (at the start of the post) when this kind of interaction is embodied by a social robot like Jibo, the experience is compelling - especially when the app/robot remembers information asserted by the user. 164 | 165 | The example suggests that knowledge graphs may offer a path to take to making automated dialog significantly more human-like 166 | 167 | ### Appendix: NLU Setup Details 168 | 169 | #### LUIS 170 | 171 | ![LUIS](./img/LUIS-kg-apps.png) 172 | 173 | ![LUIS](./img/LUIS-kg-intents.png) 174 | 175 | ![LUIS](./img/LUIS-kg-intent-launchDoYouLike.png) 176 | 177 | ![LUIS](./img/LUIS-kg-intent-launchUserLikes.png) 178 | 179 | ![LUIS](./img/LUIS-kg-entities.png) 180 | 181 | ![LUIS](./img/LUIS-kg-entity-thing.png) 182 | 183 | ![LUIS](./img/LUIS-kg-entity-user.png) 184 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | testEnvironment: 'node', 4 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "knowledge-base-neo4j", 3 | "version": "0.0.1", 4 | "description": "", 5 | "main": "dist/index.js", 6 | "scripts": { 7 | "test": "jest", 8 | "build": "tsc", 9 | "start-js": "node dist/index.js", 10 | "debug-js": "node dist/index.js -d", 11 | "start": "ts-node src/index.ts", 12 | "debug": "ts-node src/index.ts -d", 13 | "test-neo4j": "ts-node ./tools/test-neo4j.ts", 14 | "test-node-nlp": "ts-node ./tools/test-node-nlp.ts", 15 | "test-luis-controller": "ts-node ./tools/test-luis-controller.ts", 16 | "test-dialog-manager": "ts-node ./tools/test-dialog-manager.ts" 17 | }, 18 | "author": "Andrew Rapo ", 19 | "license": "MIT", 20 | "dependencies": { 21 | "commander": "^6.2.0", 22 | "inquirer": "^7.3.3", 23 | "jest": "^26.6.3", 24 | "jsonfile": "^6.1.0", 25 | "neo4j-driver": "^4.2.1", 26 | "node-nlp": "^4.16.0", 27 | "prettyjson": "^1.2.1", 28 | "request": "^2.88.2" 29 | }, 30 | "devDependencies": { 31 | "@types/jest": "^26.0.15", 32 | "@types/node": "^14.14.9", 33 | "ts-jest": "^26.4.4", 34 | "ts-node": "^9.0.0", 35 | "typescript": "^4.1.2" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/DialogManager.ts: -------------------------------------------------------------------------------- 1 | import NLUController, { 2 | NLURequestOptions, 3 | NLUIntentAndEntities 4 | } from './nlu/NLUController'; 5 | 6 | import NodeNlpController from './nlu/node-nlp/NodeNlpController'; 7 | import LUISController from './nlu/microsoft/LUISController'; 8 | 9 | import Neo4jController, { Neo4jControllerOptions } from './neo4j/Neo4jController'; 10 | 11 | import { d3Types } from './d3/d3Types'; 12 | 13 | const prettyjson = require('prettyjson'); 14 | 15 | export type DialogManagerOptions = { 16 | config: { 17 | nluType: string; 18 | luis: any; 19 | neo4j: any; 20 | }; 21 | debug: boolean; 22 | } 23 | 24 | export default class DialogManager { 25 | 26 | public neo4jController: Neo4jController; 27 | public nluController: NLUController; 28 | public sessionId: string = `robot_${Math.floor(Math.random() * 10000)}`; 29 | public languageCode: string = 'en-US'; 30 | 31 | private _debug: boolean = false; 32 | 33 | constructor() { 34 | 35 | } 36 | 37 | async init(options: DialogManagerOptions) { 38 | if (options.debug) { 39 | this._debug = true; 40 | } 41 | if (options.config.nluType === 'luis') { 42 | const luisConfig: any = { 43 | Microsoft: { 44 | nluLUIS_endpoint: options.config.luis.endpoint, 45 | nluLUIS_appId: options.config.luis.appId, 46 | nluLUIS_subscriptionKey: options.config.luis.subscriptionKey, 47 | } 48 | } 49 | this.nluController = new LUISController({ debug: this._debug, config: luisConfig }); 50 | } else { 51 | this.nluController = new NodeNlpController({ debug: this._debug }); 52 | await this.nluController.init(); 53 | } 54 | const neo4jOptions: Neo4jControllerOptions = { 55 | debug: this._debug, 56 | config: options.config.neo4j 57 | }; 58 | this.neo4jController = new Neo4jController(neo4jOptions); 59 | } 60 | 61 | ask(question: string, context?: string): Promise { 62 | return new Promise((resolve, reject) => { 63 | const options: NLURequestOptions = { 64 | languageCode: this.languageCode, 65 | contexts: [context], 66 | sessionId: this.sessionId 67 | } 68 | this.nluController.getIntentAndEntities(question, options) 69 | .then((intentAndEntities: NLUIntentAndEntities) => { 70 | if (this._debug) { 71 | console.log(`DialogManager: ask: intentAndEntities:`, JSON.stringify(intentAndEntities, null, 2)); 72 | } 73 | this.handleNLUIntentAndEntities(intentAndEntities) 74 | .then((answer) => { 75 | resolve(answer); 76 | }) 77 | .catch((err: any) => { 78 | reject(err); 79 | }); 80 | }) 81 | .catch((err: any) => { 82 | reject(err); 83 | }); 84 | }); 85 | } 86 | 87 | handleNLUIntentAndEntities(intentAndEntities: NLUIntentAndEntities): Promise { 88 | return new Promise((resolve, reject) => { 89 | let intent: string = intentAndEntities.intent; 90 | let entities: any = intentAndEntities.entities; 91 | let answer: string; 92 | 93 | this.debug(prettyjson.render(intentAndEntities, {})); 94 | 95 | if (intent == 'launchDoYouLike') { 96 | console.log(`INTENT: launchDoYouLike`); 97 | let cypher: string = `match (a {name:'${entities.thing}'})<-[:LIKES]-(j:Robot {name:'global'}) return a`; 98 | this.debug(` STEP 1a: SEE IF THE ROBOT LIKES THAT NODE ALREADY...`) 99 | this.debug(` cypher: ${cypher}`); 100 | this.neo4jController.parseCypherWithD3Helper(cypher) 101 | .then((data: d3Types.d3Graph) => { 102 | this.debug(` cypher result:`, data); 103 | if (data.nodes.length == 1) { // when successful, there will be one matching node as long as the entity names are unique 104 | let node: d3Types.d3Node = data.nodes[0]; 105 | let scriptedResponse: string = node.properties['RobotLikes']; 106 | answer = scriptedResponse ? scriptedResponse : `Yes, I do like ${entities.thingOriginal} very much.`; 107 | 108 | cypher = `match ({name:'${entities.thing}'})<-[:LIKES]-(user:User) return user`; 109 | this.debug(` STEP 1a: SEE IF THERE IS A USER THAT ALSO LIKES THAT NODE...`) 110 | this.debug(` cypher: ${cypher}`); 111 | this.neo4jController.parseCypherWithD3Helper(cypher) 112 | .then((data: d3Types.d3Graph) => { 113 | this.debug(` cypher result:`, data); 114 | if (data.nodes.length > 0) { 115 | answer += this.generateListWithPhrases(data.nodes, '...and I know that', 'likes them too.', 'like them too.'); 116 | } 117 | resolve(answer); 118 | }) 119 | .catch(() => { 120 | reject(); 121 | }); 122 | } else { 123 | let cypher = `match(v {name:'${entities.thing}'})<-[:IS_A *]-(p)<-[l:LIKES]-(j:Robot {name:'global'}) return p`; 124 | this.debug(` STEP 2: SEE IF THERE ARE DESCENDANTS OF THAT NODE LIKED BY THE ROBOT...`) 125 | this.debug(` cypher: ${cypher}`); 126 | this.neo4jController.parseCypherWithD3Helper(cypher) 127 | .then((data: d3Types.d3Graph) => { 128 | this.debug(` cypher result:`, data); 129 | let nodes: d3Types.d3Node[] = data.nodes; 130 | if (nodes.length > 1) { 131 | answer = `Yes I like many ${entities.thingOriginal} and in particular i love`; 132 | answer += this.pluralList(nodes); 133 | resolve(answer); 134 | } else if (nodes.length == 1) { 135 | let node: d3Types.d3Node = nodes[0]; 136 | answer = `Yes, of all the ${entities.thingOriginal} i like ${this.getPluralNameWithNode(node)}.`; 137 | resolve(answer); 138 | } else { // try upward inference 139 | cypher = `match (a {name:'${entities.thing}'})-[:IS_A]->(b) with b match (b)<-[:IS_A *]-(p)<-[:LIKES]-(j:Robot {name:'global'}) return b, p` 140 | this.debug(` STEP 3: TRY AN UPWARD REFERENCE...`) 141 | this.debug(` cypher: ${cypher}`); 142 | this.neo4jController.parseCypherWithD3Helper(cypher) 143 | .then((data: d3Types.d3Graph) => { 144 | this.debug(` cypher result:`, data); 145 | answer = `Actually I don't know if I like ${entities.thingOriginal}.`; 146 | let nodes: d3Types.d3Node[] = data.nodes; 147 | if (nodes.length > 1) { // the first node will be the parent and its children will be other instances of that type 148 | let parentNode: d3Types.d3Node = nodes.shift(); 149 | answer = `I don't know if I like ${entities.thingOriginal} but they are ${this.getPluralNameWithNode(parentNode)}`; 150 | answer = `${answer} and I do like ${this.getPluralNameWithNode(parentNode)}. Of all the ${this.getPluralNameWithNode(parentNode)}`; 151 | answer = `${answer} I like`; 152 | if (nodes.length > 1) { 153 | answer += this.pluralList(nodes); 154 | } else { 155 | answer = `${answer} ${this.getPluralNameWithNode(nodes[0])}.`; 156 | } 157 | } 158 | resolve(answer); 159 | }) 160 | .catch(() => { 161 | reject(); 162 | }); 163 | } 164 | }); 165 | } 166 | }) 167 | .catch((error: any) => { 168 | console.error(error); 169 | reject(error); 170 | }); 171 | 172 | } else if (intent == 'launchUserLikes') { 173 | let answer = `OK. I understand that ${entities.user} likes ${entities.thingOriginal}. That's cool!`; 174 | 175 | let cypher = `merge (user:User {name:'${entities.user}'})`; 176 | this.debug(` STEP 1: CREATE USER NODE IF IT DOES NOT EXIST YET...`) 177 | this.debug(` cypher: ${cypher}`); 178 | this.neo4jController.call(cypher) 179 | .then((data: any) => { 180 | // Make a LIKE relationship 181 | cypher = `match (like {name:'${entities.thing}'}) with like match(user:User {name:'${entities.user}'}) with like, user merge (user)-[:LIKES]->(like)`; 182 | this.debug(` STEP 1a: CREATE A LIKE RELATIONSHIP...`) 183 | this.debug(` cypher: ${cypher}`); 184 | this.neo4jController.call(cypher) 185 | .then((data: any) => { 186 | resolve(answer); 187 | }) 188 | .catch(() => { 189 | reject(); 190 | }); 191 | }) 192 | .catch(() => { 193 | reject(); 194 | }); 195 | } else { 196 | reject(); 197 | } 198 | }); 199 | } 200 | 201 | getPluralNameWithNode(node: d3Types.d3Node): string { 202 | let result: string = `${node.properties.name}s`; 203 | if (node.properties.plural) { 204 | result = node.properties.plural; 205 | } 206 | return result; 207 | } 208 | 209 | pluralList(nodes: d3Types.d3Node[]): string { 210 | let result: string = ''; 211 | for (let i: number = 0; i < (nodes.length - 1); i++) { 212 | let node = nodes[i]; 213 | result += ` ${this.getPluralNameWithNode(node)}`; 214 | if (nodes.length > 2) { 215 | result += ',' 216 | } 217 | }; 218 | result += ` and ${this.getPluralNameWithNode(nodes[nodes.length - 1])}.` 219 | return result; 220 | } 221 | 222 | generateListWithPhrases(nodes: d3Types.d3Node[], introPhrase: string = '', singularPhrase: string = '', pluralPhrase: string = ''): string { 223 | let result: string = ''; 224 | if (nodes.length > 0) { 225 | result += introPhrase; 226 | if (nodes.length == 1) { 227 | result += ` ${nodes[0].properties['name']} ${singularPhrase}`; 228 | } else { 229 | for (let i: number = 0; i < (nodes.length - 1); i++) { 230 | let node = nodes[i]; 231 | result += ` ${node.properties['name']}`; 232 | if (nodes.length > 2) { 233 | result += ','; 234 | } 235 | }; 236 | result += ` and ${nodes[nodes.length - 1].properties['name']}`; 237 | result += ` ${pluralPhrase}`; 238 | } 239 | } 240 | return result; 241 | } 242 | 243 | deleteUsers(): Promise { 244 | return new Promise((resolve, reject) => { 245 | let cypher: string = `match (n:User)-[r:LIKES]->() delete n, r`; 246 | this.neo4jController.call(cypher) 247 | .then(() => { 248 | this.debug(`The User nodes have been deleted.`); 249 | resolve(); 250 | }); 251 | }); 252 | } 253 | 254 | debug(text: string, object?: any): void { 255 | if (this._debug) { 256 | if (text && object) { 257 | console.log(text, object); 258 | } else { 259 | console.log(text); 260 | } 261 | } 262 | } 263 | } 264 | -------------------------------------------------------------------------------- /src/d3/d3Types.ts: -------------------------------------------------------------------------------- 1 | export namespace d3Types { 2 | export type d3Node = { 3 | id: string, 4 | group?: number, 5 | properties?: any, 6 | labels?: string[] 7 | }; 8 | 9 | export type d3Link = { 10 | source: string, 11 | target: string, 12 | value?: number, 13 | id?: string, 14 | type?: string, 15 | startNode?: string, 16 | endNode?: string, 17 | properties?: any, 18 | linknum?: number 19 | }; 20 | 21 | export type d3Graph = { 22 | nodes: d3Node[], 23 | links: d3Link[] 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import DialogManager from './DialogManager'; 2 | 3 | const path = require('path'); 4 | const jsonfile = require('jsonfile'); 5 | const program = require('commander'); 6 | const inquirer = require('inquirer'); 7 | 8 | const configPath = path.resolve('data/config.json'); 9 | let config: any; 10 | try { 11 | config= jsonfile.readFileSync(configPath); 12 | } catch (error) { 13 | console.error(`Error: data/config.json not found.`); 14 | process.exit(0); 15 | } 16 | 17 | let dialogManager: DialogManager; 18 | 19 | program 20 | .version('0.0.1') 21 | .description('An application testing dialog interactions') 22 | .option('-d, --debug', 'Turn on debug messages') 23 | .option('-c, --context ') 24 | .option('-r, --reset', 'Reset Users') 25 | .parse(process.argv); 26 | 27 | let context: string = 'launch' 28 | let contexts: string[] = [context]; 29 | 30 | if (program.context) { 31 | context = program.context; 32 | contexts = [program.context]; 33 | } 34 | 35 | dialogManager = new DialogManager(); 36 | dialogManager.init({ debug: program.debug, config: config }) 37 | .then(() => { 38 | if (program.reset) { 39 | console.log('>>>Cleaning up User nodes'); 40 | dialogManager.deleteUsers(); 41 | } 42 | console.log(`Ask a do-you-like question or say "[user] likes [something]".`) 43 | mainPromptLoop('>'); 44 | }); 45 | 46 | function mainPrompt(input: string) { 47 | let result: any = input; 48 | if (typeof input === 'string') { 49 | result = { 50 | type: 'command', 51 | name: 'mainInput', 52 | message: `${input}`, 53 | }; 54 | } 55 | return result; 56 | } 57 | 58 | function mainPromptLoop(msg: string) { 59 | inquirer.prompt(mainPrompt(msg)).then((answers: any) => { 60 | const input = answers.mainInput; 61 | if (input === 'quit' || input === 'bye' || input === 'exit' || input === 'x' || input === 'q') { 62 | console.log('bye'); 63 | process.exit(0); 64 | } else { 65 | dialogManager.ask(input, context) 66 | .then((result: string) => { 67 | console.log(`${result}`); 68 | mainPromptLoop('>'); 69 | }) 70 | .catch(() => { 71 | mainPromptLoop('Please try that again >'); 72 | }); 73 | } 74 | }) 75 | .catch((error: any) => { 76 | console.log(error); 77 | process.exit(0); 78 | }); 79 | } 80 | -------------------------------------------------------------------------------- /src/neo4j/Neo4jController.ts: -------------------------------------------------------------------------------- 1 | const neo4j = require('neo4j-driver'); 2 | 3 | import D3Helper from './helpers/D3Helper'; 4 | 5 | export type Neo4jControllerOptions = { 6 | config: any; 7 | debug: boolean; 8 | } 9 | 10 | export default class Neo4jController { 11 | 12 | public driver: any; 13 | 14 | protected _debug: boolean; 15 | 16 | constructor(options: Neo4jControllerOptions) { 17 | this._debug = false; 18 | if (options.debug) { 19 | this._debug = true; 20 | } 21 | this.driver = neo4j.driver(options.config.url, neo4j.auth.basic(options.config.user, options.config.password)); 22 | } 23 | 24 | call(cypher:string, params?: any): Promise { 25 | return new Promise((resolve, reject) => { 26 | let session: any = this.driver.session(); 27 | session.run(cypher, params) 28 | .then(function (result: any) { 29 | session.close(); 30 | resolve(result); 31 | }) 32 | .catch(function (error: any) { 33 | reject(error); 34 | }); 35 | }); 36 | } 37 | 38 | parseCypherWithD3Helper(cypher:string, params?: any): Promise { 39 | return new Promise((resolve, reject) => { 40 | this.call(cypher, params) 41 | .then(response => { 42 | resolve(D3Helper.data(response)); 43 | }) 44 | .catch(error => { 45 | reject(error); 46 | }); 47 | }); 48 | } 49 | 50 | getNodesWithPropertyAndValue(property: string, value: string): Promise { 51 | return new Promise((resolve, reject) => { 52 | let cypher: string = ` 53 | MATCH (n {${property}: "${value}"})-[r]-(p) 54 | return n,r,p 55 | `; 56 | this.call(cypher) 57 | .then(response => { 58 | resolve(D3Helper.data(response)); 59 | }) 60 | .catch(error => { 61 | reject(error); 62 | }); 63 | }); 64 | } 65 | 66 | 67 | 68 | updateNodeWithIdAndProperties(id: number, properties: any): Promise { 69 | return new Promise((resolve, reject) => { 70 | let cypher: string = ` 71 | match (n) WHERE ID(n) = ${id} 72 | set n = { props } 73 | `; 74 | this.call(cypher, {props: properties}) 75 | .then(response => { 76 | resolve(D3Helper.data(response)); 77 | }) 78 | .catch(error => { 79 | reject(error); 80 | }); 81 | }); 82 | } 83 | 84 | test() { 85 | this.call('MATCH (n) return n LIMIT 10') 86 | .then(result => { 87 | console.log(result); 88 | }) 89 | .catch(error => { 90 | console.log(error); 91 | }) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/neo4j/helpers/BoltToD3.ts: -------------------------------------------------------------------------------- 1 | const neo4j = require('neo4j-driver'); 2 | 3 | export default class BoltToD3 { 4 | 5 | // public neo4j: any; 6 | public nodeDict: any; 7 | public nodes: any[]; 8 | public relationships: any[]; 9 | 10 | constructor() { 11 | } 12 | 13 | isLink(field: any) { 14 | return field.start && field.end; 15 | } 16 | 17 | isNode(field: any) { 18 | return !this.isLink(field); 19 | } 20 | 21 | parse(boltResponse: any) { 22 | let i; 23 | this.nodeDict = {}; 24 | this.nodes = []; 25 | this.relationships = []; 26 | for (i = 0; i < boltResponse.records.length; i++) { 27 | // console.log(`Parsing node ${i}`); 28 | this.parseFields(boltResponse.records[i]._fields); 29 | } 30 | return { 31 | nodes: this.nodes, 32 | links: this.relationships 33 | } 34 | }; 35 | 36 | // { 37 | // "id": "3", 38 | // "labels": ["Address"], 39 | // "properties": { 40 | // "zipCode": "90210", 41 | // "country": "US", 42 | // "city": "Beverly Hills", 43 | // "state": "CA" 44 | // } 45 | // } 46 | 47 | makeNode(field: any) { 48 | let id = this.getId(field); 49 | // console.log(`makeNode: ${id}`, field); 50 | let props = this.convertNumberProps(field.properties); 51 | if (!this.nodeDict[id]) { 52 | this.nodes.push({ 53 | id: `${id}`, 54 | labels: field.labels, 55 | properties: props, 56 | group: 1 57 | }); 58 | this.nodeDict[id] = true; 59 | } 60 | return id; 61 | }; 62 | 63 | // { 64 | // "id": "13", 65 | // "type": "HAS_EMAIL", 66 | // "startNode": "1", 67 | // "endNode": "14", 68 | // "properties": {}, 69 | // "source": "1", 70 | // "target": "14", 71 | // "linknum": 1 72 | // } 73 | 74 | makeLink(field: any, id1: number, id2: number) { 75 | let id = this.getId(field); 76 | let props = this.convertNumberProps(field.properties); 77 | this.relationships.push({ 78 | id: `${id}`, 79 | type: field.type, 80 | startNode: `${id1}`, 81 | endNode: `${id2}`, 82 | properties: props, 83 | source: `${id1}`, 84 | target: `${id2}`, 85 | value: 1, 86 | linknum: 1 87 | }); 88 | }; 89 | 90 | convertNumberProps(props: any) { 91 | for (let key in props) { 92 | let prop = props[key]; 93 | if (neo4j.isInt(prop)) { 94 | props[key] = { 95 | raw: prop, 96 | converted: this.convertInt(prop) 97 | }; 98 | } 99 | } 100 | return props; 101 | }; 102 | 103 | convertInt(neoInt: any) { 104 | return neo4j.integer.inSafeRange(neoInt) ? neo4j.integer.toNumber(neoInt) : neoInt; 105 | }; 106 | 107 | getId(field: any): number { 108 | return this.convertInt(field.identity); 109 | }; 110 | 111 | // Beware: IDs/identities in neo4j are unique to their type (so a node and link could have the same ID) 112 | parseFields(fields: any[]) { 113 | // console.log(`parseFields: `, fields); 114 | let neoIdDict = {}; 115 | // first we parse the nodes 116 | for (let i: number = 0; i < fields.length; i++) { 117 | let field = fields[i]; 118 | let id = this.getId(field); 119 | let neoId = (this.isNode(field) ? 'node' : 'link') + field.identity.toString(); 120 | neoIdDict[neoId] = this.isNode(field) ? this.makeNode(field) : field; 121 | } 122 | // console.log(neoIdDict); 123 | // now we have valid node IDs and a dictionary, we can parse the links 124 | for (let key in neoIdDict) { 125 | let field = neoIdDict[key]; 126 | if (this.isLink(field)) { 127 | let start = this.convertInt(field.start); 128 | let end = this.convertInt(field.end); 129 | this.makeLink(field, start, end); 130 | } 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/neo4j/helpers/D3Helper.ts: -------------------------------------------------------------------------------- 1 | import BoltToD3 from './BoltToD3'; 2 | 3 | export default class PartnersGraphHelper { 4 | 5 | static data(cypherResponse: any): any[] { 6 | let result: any = {}; 7 | 8 | // console.log(cypherResponse); 9 | let parser = new BoltToD3(); 10 | result = parser.parse(cypherResponse) 11 | 12 | return result; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/neo4j/index.ts: -------------------------------------------------------------------------------- 1 | import Neo4jController from './Neo4jController'; 2 | import D3Helper from './helpers/D3Helper'; 3 | 4 | export default Neo4jController; 5 | 6 | export { 7 | D3Helper 8 | } 9 | -------------------------------------------------------------------------------- /src/nlu/NLUController.ts: -------------------------------------------------------------------------------- 1 | export type NLUIntentAndEntities = { 2 | intent: string; 3 | intents: any; 4 | entities: any; 5 | response: any; 6 | } 7 | 8 | export type NLURequestOptions = { 9 | languageCode?: string; 10 | contexts?: string[]; 11 | sessionId?: string; 12 | } 13 | 14 | export enum NLULanguageCode { 15 | en_US = 'en-US' 16 | } 17 | 18 | export type NLUControllerOptions = { 19 | config?: any; 20 | debug?: boolean; 21 | } 22 | 23 | export default abstract class NLUController { 24 | 25 | protected _debug: boolean; 26 | 27 | constructor(options: NLUControllerOptions = {}) { 28 | this._debug = false; 29 | if (options.debug) { 30 | this._debug = true; 31 | } 32 | } 33 | 34 | abstract init(): Promise; 35 | 36 | abstract set config(config: any); 37 | 38 | abstract call(query: string, languageCode: string, context: string, sessionId?: string): Promise; 39 | 40 | abstract getEntitiesWithResponse(response: any): any | undefined; 41 | 42 | abstract getIntentAndEntities(utterance: string, options?: NLURequestOptions): Promise; 43 | } 44 | -------------------------------------------------------------------------------- /src/nlu/microsoft/LUISController.ts: -------------------------------------------------------------------------------- 1 | import NLUController, { NLUControllerOptions, NLUIntentAndEntities, NLURequestOptions, NLULanguageCode } from '../NLUController'; 2 | 3 | const request = require('request'); 4 | const querystring = require('querystring'); 5 | 6 | export type LUISIntent = { 7 | [intent: string]: { 8 | score: number; 9 | } 10 | }; 11 | 12 | export type LUISEntity = { 13 | [entity: string]: []; 14 | } 15 | 16 | export type LUISResponse = { 17 | query: string; 18 | prediction: any; 19 | intents: LUISIntent[]; 20 | entities: LUISEntity; 21 | } 22 | 23 | export default class LUISController extends NLUController { 24 | 25 | public endpoint: string = ''; 26 | public luisAppId: string = ''; 27 | public subscriptionKey: string = ''; 28 | 29 | private _config: any = {}; 30 | 31 | constructor(options: NLUControllerOptions = {}) { 32 | super(options); 33 | this.config = options.config; 34 | } 35 | 36 | init(): Promise { 37 | return Promise.resolve(); 38 | } 39 | 40 | set config(config: any) { 41 | if (config && config.Microsoft && (config.Microsoft.nluLUIS_endpoint || config.Microsoft.LuisEndpoint) && (config.Microsoft.nluLUIS_appId || config.Microsoft.LuisAppId) && (config.Microsoft.nluLUIS_subscriptionKey || config.Microsoft.LuisSubscriptionKey)) { 42 | this._config = config; 43 | this.endpoint = this._config.Microsoft.nluLUIS_endpoint || config.Microsoft.LuisEndpoint; 44 | this.luisAppId = this._config.Microsoft.nluLUIS_appId || config.Microsoft.LuisAppId; 45 | this.subscriptionKey = this._config.Microsoft.nluLUIS_subscriptionKey || config.Microsoft.LuisSubscriptionKey; 46 | } else { 47 | throw new Error(`LUISController: set config: error: incomplete config:`); 48 | } 49 | } 50 | 51 | call(query: string): Promise { 52 | let endpoint = this.endpoint; 53 | let luisAppId = this.luisAppId; 54 | let queryParams = { 55 | "subscription-key": this.subscriptionKey, 56 | "timezoneOffset": "0", 57 | "verbose": true, 58 | "query": query 59 | } 60 | 61 | let luisRequest = `${endpoint}luis/prediction/v3.0/apps/${luisAppId}/slots/production/predict?` + querystring.stringify(queryParams); 62 | if (this._debug) { 63 | console.log(luisRequest); 64 | } 65 | return new Promise((resolve, reject) => { 66 | request(luisRequest, 67 | ((error: string, response: any, body: any) => { 68 | if (error) { 69 | if (this._debug) { 70 | console.log(`LUISController: call: error:`, error, response); 71 | } 72 | reject(error); 73 | } else { 74 | let body_obj: any = JSON.parse(body); 75 | resolve(body_obj); 76 | } 77 | })); 78 | }); 79 | } 80 | 81 | /* 82 | "entities": { 83 | "thing": [ 84 | [ 85 | "Mammal" 86 | ] 87 | ], 88 | "$instance": { 89 | "thing": [ 90 | { 91 | "type": "thing", 92 | "text": "mammals", 93 | "startIndex": 12, 94 | "length": 7, 95 | "modelTypeId": 5, 96 | "modelType": "List Entity Extractor", 97 | "recognitionSources": [ 98 | "model" 99 | ] 100 | } 101 | ] 102 | } 103 | } 104 | */ 105 | 106 | getEntitiesWithResponse(response: LUISResponse): any { 107 | let entitiesObject: any = { 108 | user: 'Someone', 109 | userOriginal: 'Someone', 110 | thing: 'that', 111 | thingOriginal: 'that' 112 | }; 113 | 114 | if (response.prediction && response.prediction.entities && response.prediction.entities['$instance']) { 115 | const entityKeys: string[] = Object.keys(response.prediction.entities); 116 | entityKeys.forEach((entityKey: string) => { 117 | if (entityKey !== '$instance') { 118 | if (this._debug) console.log(entityKey); 119 | const entity: string[] = response.prediction.entities[entityKey][0]; 120 | entitiesObject[entityKey] = entity[0]; 121 | } 122 | }); 123 | const instanceKeys: string[] = Object.keys(response.prediction.entities['$instance']); 124 | instanceKeys.forEach((instanceKey: string) => { 125 | if (this._debug) console.log(instanceKey); 126 | const entityObjects: any[] = response.prediction.entities['$instance'][instanceKey]; 127 | if (this._debug) console.log(`entityObject:`, entityObjects[0]); 128 | const originalKey: string = `${instanceKey}Original`; 129 | entitiesObject[originalKey] = entityObjects[0].text; 130 | }); 131 | } 132 | return entitiesObject; 133 | } 134 | 135 | getIntentAndEntities(utterance: string, options?: NLURequestOptions): Promise { 136 | options = options || {}; 137 | let defaultOptions: NLURequestOptions = { 138 | languageCode: NLULanguageCode.en_US, 139 | contexts: undefined, 140 | sessionId: undefined 141 | } 142 | options = Object.assign(defaultOptions, options); 143 | 144 | return new Promise((resolve, reject) => { 145 | this.call(utterance) 146 | .then((response: LUISResponse) => { 147 | let intentAndEntities: NLUIntentAndEntities = { 148 | intent: '', 149 | intents: response.prediction.intents, 150 | entities: this.getEntitiesWithResponse(response), 151 | response: response 152 | } 153 | if (response && response.prediction && response.prediction.topIntent) { 154 | intentAndEntities.intent = response.prediction.topIntent 155 | } else { 156 | if (this._debug) { 157 | console.log(`LUISController: getIntentAndEntities: unknown response format:`); 158 | } 159 | // console.log(response); 160 | } 161 | resolve(intentAndEntities); 162 | }) 163 | .catch((err: any) => { 164 | reject(err); 165 | }); 166 | }); 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/nlu/node-nlp/NodeNlpController.test.ts: -------------------------------------------------------------------------------- 1 | import NodeNlpController from './NodeNlpController'; 2 | import { NLUIntentAndEntities } from '../NLUController'; 3 | 4 | let nodeNlpController: NodeNlpController; 5 | 6 | // Note: To regenerate the current model, delete (or rename) ../../../data/model.nlp 7 | 8 | test('NodeNlpController: instantiate and ready', async () => { 9 | nodeNlpController = new NodeNlpController(); 10 | await nodeNlpController.init(); 11 | expect(nodeNlpController.ready).toBeTruthy; 12 | }); 13 | 14 | test('NodeNlpController: parse intent: launchUserLikes', async () => { 15 | nodeNlpController.getIntentAndEntities('Andrew likes penguins') 16 | .then((result: NLUIntentAndEntities) => { 17 | expect(result.intent).toEqual('launchUserLikes'); 18 | }); 19 | }); 20 | 21 | /* 22 | { 23 | intent: 'launchUserLikes', 24 | entities: 25 | { 26 | user: 'Andrew', 27 | userOriginal: 'Andrew', 28 | thing: 'Penguins', 29 | thingOriginal: 'penguins', 30 | entities: [[Object], [Object]] 31 | } 32 | } 33 | */ 34 | -------------------------------------------------------------------------------- /src/nlu/node-nlp/NodeNlpController.ts: -------------------------------------------------------------------------------- 1 | import NLUController, { NLUControllerOptions, NLUIntentAndEntities, NLURequestOptions, NLULanguageCode } from '../NLUController'; 2 | 3 | const path = require('path'); 4 | const { NlpManager } = require('node-nlp'); 5 | 6 | export type NodeNlpIntent = { 7 | intent: string; 8 | score: number; 9 | }; 10 | 11 | export type NodeNlpEntity = { 12 | start: number; 13 | end: number; 14 | len: number; 15 | accuracy: number; 16 | entity: string; 17 | type: string; 18 | option: string; 19 | sourceText: string; 20 | utteranceText: string; 21 | resolution: any; 22 | } 23 | 24 | export type NodeNlpClassification = { 25 | intent: string; 26 | score: number; 27 | } 28 | 29 | export type NodeNlpSentiment = { 30 | score: number; 31 | numWords: number; 32 | numHits: number; 33 | average: number; 34 | type: string; 35 | locale: string; 36 | vote: string; 37 | } 38 | 39 | export type NodeNlpResponse = { 40 | locale: string; 41 | utterance: string; 42 | languageGuessed: boolean; 43 | localeIso2: string; 44 | language: string; 45 | classifications: NodeNlpClassification[]; 46 | intent: string; 47 | score: number; 48 | domain: string; 49 | sourceEntities: string[]; 50 | entities: NodeNlpEntity[]; 51 | answers: string[]; 52 | answer: undefined; 53 | actions: string[]; 54 | sentiment: NodeNlpSentiment; 55 | } 56 | 57 | export default class NodeNlpController extends NLUController { 58 | 59 | private _classifier: any; 60 | private _ready: boolean; 61 | 62 | /** 63 | * @constructor 64 | */ 65 | constructor(options: NLUControllerOptions = {}) { 66 | super(options); 67 | this._ready = false; 68 | } 69 | 70 | get ready(): boolean { 71 | return this._ready; 72 | } 73 | 74 | get classifier(): any { 75 | return this._classifier; 76 | } 77 | 78 | set config(config: any) { 79 | if (config) { 80 | // 81 | } else { 82 | // 83 | } 84 | } 85 | 86 | async init(modelPath: string = '') { 87 | this._classifier = new NlpManager({ languages: ['en'], autoSave: false, nlu: { log: false } }); 88 | if (modelPath) { 89 | let inputPath: string = path.resolve(modelPath); // (__dirname, '../../../data/model.nlp'); 90 | if (this._debug) { 91 | console.info(`NodeNlpController: init: loading: ${inputPath}`); 92 | } 93 | try { 94 | this._classifier.load(inputPath); 95 | this._ready = true; 96 | } catch (err) { 97 | if (this._debug) { 98 | console.info(`NodeNlpController: init: model NOT FOUND: ${inputPath}`); 99 | } 100 | } 101 | } 102 | if (!this._ready) { 103 | if (this._debug) { 104 | console.info(`NodeNlpController: generating model...`); 105 | } 106 | await this.generateModel(); 107 | this._ready = true; 108 | } 109 | } 110 | 111 | generateModel(outputPath: string = '') { 112 | if (this._debug) { 113 | console.info(`NodeNlpController: generateModel: outputPath: ${outputPath}`); 114 | } 115 | return new Promise(async (resolve, reject) => { 116 | 117 | this._classifier.addNamedEntityText('user', 'Andrew', ['en'], ['Andrew', 'andrew', 'andy']); 118 | this._classifier.addNamedEntityText('user', 'Robert', ['en'], ['Robert', 'robert', 'bob']); 119 | this._classifier.addNamedEntityText('user', 'Jane', ['en'], ['Jane', 'jane']); 120 | this._classifier.addNamedEntityText('user', 'Eric', ['en'], ['Rick', 'rick', 'eric']); 121 | this._classifier.addNamedEntityText('user', 'Cynthia', ['en'], ['Cynthia', 'cynthia']); 122 | 123 | this._classifier.addNamedEntityText('thing', 'Penguin', ['en'], ['Penguins', 'penguins', 'penguin']); 124 | this._classifier.addNamedEntityText('thing', 'Mammal', ['en'], ['Mammals', 'mammals', 'mammal']); 125 | this._classifier.addNamedEntityText('thing', 'Bird', ['en'], ['Birds', 'birds', 'bird']); 126 | this._classifier.addNamedEntityText('thing', 'Whale', ['en'], ['Whales', 'whales', 'whale']); 127 | 128 | this._classifier.addNamedEntityText('thing', 'Albatross', ['en'], ['Albatrosses', 'albatross']); 129 | this._classifier.addNamedEntityText('thing', 'Armadillo', ['en'], ['Armadillos', 'armadillo']); 130 | this._classifier.addNamedEntityText('thing', 'Flamingo', ['en'], ['Flamingos', 'flamingo']); 131 | this._classifier.addNamedEntityText('thing', 'Fly', ['en'], ['Flies', 'fly']); 132 | this._classifier.addNamedEntityText('thing', 'Zebra', ['en'], ['Zebras', 'zebra']); 133 | this._classifier.addNamedEntityText('thing', 'Mammal', ['en'], ['Mammals', 'mammal']); 134 | this._classifier.addNamedEntityText('thing', 'Bird', ['en'], ['Birds', 'bird']); 135 | this._classifier.addNamedEntityText('thing', 'Bat', ['en'], ['Bats', 'bat']); 136 | this._classifier.addNamedEntityText('thing', 'Insect', ['en'], ['Insects', 'insect']); 137 | this._classifier.addNamedEntityText('thing', 'Butterfly', ['en'], ['Butterflies', 'butterfly']); 138 | this._classifier.addNamedEntityText('thing', 'Vertebrate', ['en'], ['Vertebrates', 'vertebrate']); 139 | this._classifier.addNamedEntityText('thing', 'Animal', ['en'], ['Animals', 'animal']); 140 | this._classifier.addNamedEntityText('thing', 'Invertebrate', ['en'], ['Invertebrates', 'invertebrate']); 141 | 142 | this._classifier.addDocument('en', "%user% likes %thing%", 'launchUserLikes'); 143 | this._classifier.addDocument('en', "do you like %thing%", 'launchDoYouLike'); 144 | 145 | await this._classifier.train(); 146 | if (!outputPath) { 147 | outputPath = path.resolve(__dirname, '../../../data/model-new.nlp'); 148 | if (this._debug) { 149 | console.info(`No model outputPath specified. Using: ${outputPath}`); 150 | } 151 | } 152 | this._classifier.save(outputPath); 153 | resolve(outputPath); 154 | }); 155 | } 156 | 157 | call(query: string): Promise { 158 | return new Promise(async (resolve, reject) => { 159 | const response: NodeNlpResponse = await this._classifier.process(query); 160 | resolve(response); 161 | }); 162 | } 163 | 164 | getEntitiesWithResponse(response: NodeNlpResponse): any { 165 | let entitiesObject: any = { 166 | user: 'Someone', 167 | userOriginal: 'Someone', 168 | thing: 'that', 169 | thingOriginal: 'that', 170 | entities: undefined 171 | }; 172 | 173 | if (response && response.entities) { 174 | entitiesObject.entities = response.entities; 175 | response.entities.forEach((entity: NodeNlpEntity) => { 176 | if (entity.entity === 'user') { 177 | const user: string = entity.option || entity.utteranceText; 178 | entitiesObject.user = user; 179 | entitiesObject.userOriginal = entity.utteranceText; 180 | } else if (entity.entity === 'thing') { 181 | const thing: string = entity.option || entity.utteranceText; 182 | entitiesObject.thing = thing; 183 | entitiesObject.thingOriginal = entity.utteranceText; 184 | } 185 | }); 186 | } 187 | return entitiesObject; 188 | } 189 | 190 | getIntentAndEntities(utterance: string, options?: NLURequestOptions): Promise { 191 | options = options || {}; 192 | let defaultOptions: NLURequestOptions = { 193 | languageCode: NLULanguageCode.en_US, 194 | contexts: undefined, 195 | sessionId: undefined 196 | } 197 | options = Object.assign(defaultOptions, options); 198 | 199 | return new Promise((resolve, reject) => { 200 | let intentAndEntities: NLUIntentAndEntities = { 201 | intent: '', 202 | intents: undefined, 203 | entities: undefined, 204 | response: undefined 205 | } 206 | if (this._ready && utterance) { 207 | this.call(utterance) 208 | .then((response: NodeNlpResponse) => { 209 | if (response && response.intent) { 210 | intentAndEntities = { 211 | intent: response.intent, 212 | intents: undefined, 213 | entities: this.getEntitiesWithResponse(response), 214 | response: response, 215 | } 216 | } 217 | resolve(intentAndEntities); 218 | }) 219 | .catch((err: any) => { 220 | reject(err); 221 | }); 222 | } else { 223 | resolve(intentAndEntities); 224 | } 225 | 226 | }); 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /tools/test-dialog-manager.ts: -------------------------------------------------------------------------------- 1 | import DialogManager from '../src/DialogManager'; 2 | const program = require('commander'); 3 | 4 | program 5 | .version('0.0.1') 6 | .description('An application for testing the DialogManager class') 7 | .option('-q, --query ', 'The query to test') 8 | .option('-c, --context ') 9 | .option('-n, --nlu ', 'node-nlp, luis') 10 | .parse(process.argv); 11 | 12 | let context: string = 'launch'; 13 | let contexts: string[] = [context]; 14 | let query: string = 'do you like penguins'; 15 | let nluType: string = 'node-nlp'; 16 | 17 | if (program.context) { 18 | contexts = [program.context]; 19 | } 20 | if (program.query) { 21 | // console.log(program.query); 22 | query = program.query; 23 | } 24 | if (program.nlu) { 25 | nluType = program.nlu; 26 | } 27 | 28 | const dialogManager = new DialogManager(); 29 | dialogManager.init({debug: true, nluType: nluType}) 30 | .then(() => { 31 | dialogManager.ask(query, context) 32 | .then((result: string) => { 33 | console.log(`result:\n`, result); 34 | }) 35 | .catch((err: any) => { 36 | console.log(`error:\n`, err); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /tools/test-luis-controller.ts: -------------------------------------------------------------------------------- 1 | import LUISController from '../src/nlu/microsoft/LUISController'; 2 | 3 | const path = require('path'); 4 | const jsonfile = require('jsonfile'); 5 | 6 | const doTestLuisNlu = () => { 7 | 8 | const configPath = path.resolve('data/config.json'); 9 | let config: any; 10 | try { 11 | config= jsonfile.readFileSync(configPath); 12 | } catch (error) { 13 | console.error(`Error: data/config.json not found.`); 14 | process.exit(0); 15 | } 16 | 17 | const luisConfig: any = { 18 | Microsoft: { 19 | nluLUIS_endpoint: config.luis.endpoint, 20 | nluLUIS_appId: config.luis.appId, 21 | nluLUIS_subscriptionKey: config.luis.subscriptionKey, 22 | } 23 | } 24 | const luisController = new LUISController({ config: luisConfig, debug: true }); 25 | 26 | let timeLog = { 27 | timeStart: new Date().getTime(), 28 | complete: 0, 29 | cloudLatency: 0, 30 | } 31 | luisController.getIntentAndEntities('do you like mammals') 32 | .then((intentAndEntities: any) => { 33 | timeLog.complete = new Date().getTime(); 34 | timeLog.cloudLatency = timeLog.complete - timeLog.timeStart; 35 | console.log(`NLUIntentAndEntities: `, JSON.stringify(intentAndEntities, null, 2)); 36 | console.log(`timeLog:`, JSON.stringify(timeLog, null, 2)); 37 | }) 38 | .catch((error: any) => { 39 | console.log(error); 40 | }); 41 | } 42 | 43 | doTestLuisNlu(); 44 | -------------------------------------------------------------------------------- /tools/test-neo4j.ts: -------------------------------------------------------------------------------- 1 | import Neo4jController, {D3Helper} from '../src/neo4j'; 2 | 3 | const program = require('commander'); 4 | const prettyjson = require('prettyjson'); 5 | 6 | let neo4jController = new Neo4jController(); 7 | 8 | program 9 | .version('0.0.1') 10 | .description('An application testing neo4j cyphers') 11 | .option('-c, --cypher ', 'Specify the cypher to test') 12 | .option('--d3', 'Parse results with D3Helper') 13 | .parse(process.argv); 14 | 15 | let cypher: string = 'match (n) return n LIMIT 25'; 16 | if (program.cypher) { 17 | console.log(program.cypher); 18 | cypher = program.cypher; 19 | } 20 | 21 | let d3HelperFlag: boolean = false; 22 | if (program.d3) { 23 | console.log(program.d3); 24 | d3HelperFlag = true; 25 | } 26 | 27 | if (d3HelperFlag) { 28 | neo4jController.parseCypherWithD3Helper(cypher) 29 | .then((response: string) => { 30 | let output: string = prettyjson.render(response, {}); 31 | console.log(output); 32 | }) 33 | .catch((err) => { 34 | console.log(`ERROR: neo4jController\n`, err) 35 | }); 36 | } else { 37 | neo4jController.call(cypher) 38 | .then((response: string) => { 39 | let output: string = prettyjson.render(response, {}); 40 | console.log(output); 41 | }) 42 | .catch((err) => { 43 | console.log(`ERROR: neo4jController\n`, err) 44 | }); 45 | } 46 | -------------------------------------------------------------------------------- /tools/test-node-nlp.ts: -------------------------------------------------------------------------------- 1 | const { NlpManager } = require('node-nlp'); 2 | 3 | const manager = new NlpManager({ languages: ['en'] }); 4 | manager.addNamedEntityText( 5 | 'hero', 6 | 'spiderman', 7 | ['en'], 8 | ['Spiderman', 'Spider-man'], 9 | ); 10 | manager.addNamedEntityText( 11 | 'hero', 12 | 'iron man', 13 | ['en'], 14 | ['iron man', 'iron-man'], 15 | ); 16 | manager.addNamedEntityText('hero', 'thor', ['en'], ['Thor']); 17 | manager.addNamedEntityText( 18 | 'food', 19 | 'burguer', 20 | ['en'], 21 | ['Burguer', 'Hamburguer'], 22 | ); 23 | manager.addNamedEntityText('food', 'pizza', ['en'], ['pizza']); 24 | manager.addNamedEntityText('food', 'pasta', ['en'], ['Pasta', 'spaghetti']); 25 | manager.addDocument('en', 'I saw %hero% eating %food%', 'sawhero'); 26 | manager.addDocument( 27 | 'en', 28 | 'I have seen %hero%, he was eating %food%', 29 | 'sawhero', 30 | ); 31 | manager.addDocument('en', 'I want to eat %food%', 'wanteat'); 32 | manager.train() 33 | .then(() => { 34 | manager 35 | .process('I saw spiderman eating spaghetti today in the city!') 36 | .then((result: any) => console.log(result)); 37 | }); 38 | 39 | // { locale: 'en', 40 | // localeIso2: 'en', 41 | // language: 'English', 42 | // utterance: 'I saw spiderman eating spaghetti today in the city!', 43 | // classification: 44 | // [ { label: 'sawhero', value: 0.9920519933583061 }, 45 | // { label: 'wanteat', value: 0.00794800664169383 } ], 46 | // intent: 'sawhero', 47 | // score: 0.9920519933583061, 48 | // entities: 49 | // [ { start: 6, 50 | // end: 15, 51 | // levenshtein: 0, 52 | // accuracy: 1, 53 | // option: 'spiderman', 54 | // sourceText: 'Spiderman', 55 | // entity: 'hero', 56 | // utteranceText: 'spiderman' }, 57 | // { start: 23, 58 | // end: 32, 59 | // levenshtein: 0, 60 | // accuracy: 1, 61 | // option: 'pasta', 62 | // sourceText: 'spaghetti', 63 | // entity: 'food', 64 | // utteranceText: 'spaghetti' } ], 65 | // sentiment: 66 | // { score: 0.708, 67 | // comparative: 0.07866666666666666, 68 | // vote: 'positive', 69 | // numWords: 9, 70 | // numHits: 2, 71 | // type: 'senticon', 72 | // language: 'en' } } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist/", 4 | "sourceMap": true, 5 | "noImplicitAny": true, 6 | "suppressImplicitAnyIndexErrors": true, 7 | "module": "commonjs", 8 | "target": "es6", 9 | "jsx": "react" 10 | }, 11 | "include": [ 12 | "./src/**/*" 13 | ], 14 | "exclude": [ 15 | "./hide/**/*" 16 | ] 17 | } --------------------------------------------------------------------------------