Source: org/terraswarm/accessor/accessors/web/services/NaturalLanguage.js

  1. // Copyright (c) 2017 The Regents of the University of California.
  2. // All rights reserved.
  3. //
  4. // Permission is hereby granted, without written agreement and without
  5. // license or royalty fees, to use, copy, modify, and distribute this
  6. // software and its documentation for any purpose, provided that the above
  7. // copyright notice and the following two paragraphs appear in all copies
  8. // of this software.
  9. //
  10. // IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
  11. // FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
  12. // ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
  13. // THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
  14. // SUCH DAMAGE.
  15. //
  16. // THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
  17. // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  18. // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
  19. // PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
  20. // CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
  21. // ENHANCEMENTS, OR MODIFICATIONS.
  22. //
  23. /** Respond to textual inputs using the natural-language server from
  24. * Google called API.AI (see http://api.ai). To use this, you need to
  25. * create an agent at API.AI, using for example the tutorial here:
  26. *
  27. * https://developers.google.com/actions/develop/apiai/tutorials/getting-started
  28. *
  29. * An agent parses input text (natural language) and matches queries against
  30. * rules defined in the agent to issue responses. For example, the above
  31. * tutorial walks you through creating an agent that asks for your favorite
  32. * number and color and then constructs a silly name by concatenating the two.
  33. *
  34. * Once you have created an agent, you should set the '''clientAccessToken'''
  35. * parameter to the hex key that identifies the agent. Without this token
  36. * this accessor will not do anything.
  37. *
  38. * This accessor is just a starting point. It should be extended
  39. * to support multiple languages, for example. Also, it would be nice
  40. * if there were a public agent that we could provide a default client
  41. * access token for so that the accessor would work out of the box, without
  42. * having to go create your own agent.
  43. *
  44. * This accessor uses the apiai modeul, which is supported by at least
  45. * the Nashorn (and CapeCode) hosts and the Node.js host.
  46. * To use this under the Node.js host, install the apiai NPM module
  47. * as follows:
  48. * <pre>
  49. * npm apiai
  50. * </pre>
  51. * For the Nashorn host, CapeCode includes a port of the apiai module
  52. * that works under Nashorn.
  53. *
  54. * @accessor services/NaturalLanguage
  55. * @input {string} textQuery A query that the service is to respond to.
  56. * @output {object} response The full response from the service.
  57. * @output {string} fulfillment The fulfillment text within the response.
  58. * @parameter {string} clientAccessToken The client access token for the service.
  59. *
  60. * @author Edward A. Lee
  61. * @version $$Id$$
  62. */
  63. // Stop extra messages from jslint and jshint. Note that there should
  64. // be no space between the / and the * and global. See
  65. // https://chess.eecs.berkeley.edu/ptexternal/wiki/Main/JSHint */
  66. /*globals error, exports, extractFulfillment, require */
  67. /*jshint globalstrict: true*/
  68. 'use strict';
  69. exports.setup = function () {
  70. this.input('textQuery', {
  71. type: 'string'
  72. });
  73. this.output('response');
  74. this.output('fulfillment', {
  75. type: 'string',
  76. spontaneous: true
  77. });
  78. // Note that it is OK for the clientAccessToken to
  79. // be included in the accessor because the author needs
  80. // to configure an agent for this to work.
  81. this.parameter('clientAccessToken', {
  82. type: 'string',
  83. value: '<-- your client access token here -->'
  84. });
  85. };
  86. var apiai = require('apiai');
  87. var util = require('util');
  88. exports.initialize = function () {
  89. var self = this;
  90. var token = this.get('clientAccessToken');
  91. if (token === '<-- your client access token here -->') {
  92. error('You need to set clientAccessToken to a hex string that identifies\n' +
  93. 'an agent at https://api.ai. For a tutorial on creating an agent, see\n' +
  94. 'https://developers.google.com/actions/develop/apiai/tutorials/getting-started');
  95. return;
  96. }
  97. var app = apiai(this.get('clientAccessToken'));
  98. self.addInputHandler('textQuery', function () {
  99. // The session ID, I guess, disambiguates multiple users of the same
  100. // agent. Here, I'm just using a fixed session ID, which is risky.
  101. var request = app.textRequest(self.get('textQuery'), {
  102. sessionId: 'textQuery'
  103. });
  104. request.on('response', function (response) {
  105. self.send('response', response);
  106. self.send('fulfillment', extractFulfillment(response));
  107. });
  108. request.on('error', function (message) {
  109. error(message);
  110. });
  111. request.end();
  112. });
  113. };
  114. /** Given a response structure, find the fulfillment speech within it
  115. * and return that. If there is no fulfillment speech in the response,
  116. * then just return the entire response formatted using util.inspect().
  117. */
  118. function extractFulfillment(response) {
  119. if (response.result &&
  120. response.result.fulfillment &&
  121. response.result.fulfillment.speech) {
  122. return response.result.fulfillment.speech;
  123. }
  124. return util.inspect(response);
  125. }