diff --git a/README.md b/README.md index e1123692..61d3d158 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,35 @@ Node-RED Watson Nodes for IBM Cloud CLA assistant +### New in version 0.7.5 +- Bump SDK Dependency to 3.15.0 +- Added Portuguese (Brazilian) and Chinese (Simplified and Traditional) as output languages +for Visual Recognition node. +- Added list voices and delete customisation methods to TTS Corpus Builder node. +- STT Node Changes + - Allowing SDK to manage IAM Tokens. + - Streaming mode for STT using IAM key now working. + - Fix to stream mode for max alternatives and smart formatting options + - Keywords, Word Confidence and Customization Weight can now be specified + - Allow Start and End data packets to be specified as JSON objects, as well as +a stringified JSON objects. + - In line with SDK change use createLanguageModel() to create custom model +- Disable SSL Verification option for Assistant Node. +- Natural Language Understanding Node Changes + - Bump Natural Language Understanding to 2018-11-16 + - Add Limit Categories and limit_text_characters options +- Allow JSON input into Personality Insights node. +- Japanese word count was causing a Node-RED crash when run in the cloud. +- Hungarian supported by Language Translator. +- New Document Language Translator node. +- New Assistant V2 Node. +- Discovery Node changes + - Bump Discovery to 2018-12-03 + - Implement Query Notices method +- Bump dependency on file-type to 10.7.0 +- Bump dependency on temp to 0.9.0 + + ### New in version 0.7.4 - Bump SDK Dependency to 3.11.0 - Bump Assistant version to 2018-09-20 diff --git a/package.json b/package.json index 863ebd2b..3eb2beaa 100644 --- a/package.json +++ b/package.json @@ -1,17 +1,16 @@ { "name": "node-red-node-watson", - "version": "0.7.4", + "version": "0.7.5", "description": "A collection of Node-RED nodes for IBM Watson services", "dependencies": { "async": "^1.5.2", "cfenv": "~1.0.0", - "file-type": "^2.7.0", + "file-type": "^10.7.0", "request": "~2.86.0", - "temp": "^0.8.3", + "temp": "^0.9.0", "qs": "6.x", "image-type": "^2.0.2", - "watson-developer-cloud": "^3.11.0", - "kuromoji": "^0.1.1", + "watson-developer-cloud": "^3.15.0", "word-count": "^0.2.2", "is-docx": "^0.0.3", "stream-to-array": "^2.3.0", @@ -35,12 +34,14 @@ ], "node-red": { "nodes": { - "watson-conversation-v1": "services/conversation/v1.js", - "watson-conversation-workspace-manager-v1": "services/conversation/v1-workspace-manager.js", + "watson-conversation-v1": "services/assistant/v1.js", + "watson-conversation-workspace-manager-v1": "services/assistant/v1-workspace-manager.js", + "watson-assistant-v2": "services/assistant/v2.js", "watson-discovery-v1": "services/discovery/v1.js", "watson-discovery-v1-document-loader": "services/discovery/v1-document-loader.js", "watson-discovery-v1-query-builder": "services/discovery/v1-query-builder.js", "watson-language-translator-v3": "services/language_translator/v3.js", + "watson-doc-translator-v3": "services/language_translator/v3-doc.js", "watson-language-translator-identify-v3": "services/language_translator_identify/v3.js", "watson-language-translator-util-v3": "services/language_translator_util/v3.js", "watson-natural-language-classifier-v1": "services/natural_language_classifier/v1.js", diff --git a/services/conversation/icons/conversation-v1-25x25.png b/services/assistant/icons/conversation-v1-25x25.png similarity index 100% rename from services/conversation/icons/conversation-v1-25x25.png rename to services/assistant/icons/conversation-v1-25x25.png diff --git a/services/conversation/v1-exp.html b/services/assistant/v1-exp.html similarity index 100% rename from services/conversation/v1-exp.html rename to services/assistant/v1-exp.html diff --git a/services/conversation/v1-exp.js b/services/assistant/v1-exp.js similarity index 100% rename from services/conversation/v1-exp.js rename to services/assistant/v1-exp.js diff --git a/services/conversation/v1-workspace-manager.html b/services/assistant/v1-workspace-manager.html similarity index 100% rename from services/conversation/v1-workspace-manager.html rename to services/assistant/v1-workspace-manager.html diff --git a/services/conversation/v1-workspace-manager.js b/services/assistant/v1-workspace-manager.js similarity index 100% rename from services/conversation/v1-workspace-manager.js rename to services/assistant/v1-workspace-manager.js diff --git a/services/conversation/v1.html b/services/assistant/v1.html similarity index 100% rename from services/conversation/v1.html rename to services/assistant/v1.html diff --git a/services/conversation/v1.js b/services/assistant/v1.js similarity index 100% rename from services/conversation/v1.js rename to services/assistant/v1.js diff --git a/services/assistant/v2.html b/services/assistant/v2.html new file mode 100644 index 00000000..01f1a59f --- /dev/null +++ b/services/assistant/v2.html @@ -0,0 +1,238 @@ + + + + + + + + diff --git a/services/assistant/v2.js b/services/assistant/v2.js new file mode 100644 index 00000000..a1db637c --- /dev/null +++ b/services/assistant/v2.js @@ -0,0 +1,351 @@ +/** + * Copyright 2018 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +module.exports = function(RED) { + const SERVICE_IDENTIFIER = 'assistant', + OLD_SERVICE_IDENTIFIER = 'conversation', + SERVICE_VERSION = '2018-11-08'; + + var pkg = require('../../package.json'), + AssistantV2 = require('watson-developer-cloud/assistant/v2'), + serviceutils = require('../../utilities/service-utils'), + payloadutils = require('../../utilities/payload-utils'), + service = null, + sApikey = null, + sUsername = null, + sPassword = null; + + service = serviceutils.getServiceCreds(SERVICE_IDENTIFIER); + if (!service) { + service = serviceutils.getServiceCreds(OLD_SERVICE_IDENTIFIER); + } + + if (service) { + sUsername = service.username ? service.username : ''; + sPassword = service.password ? service.password : ''; + sApikey = service.apikey ? service.apikey : ''; + } + + RED.httpAdmin.get('/watson-assistant-v2/vcap', function(req, res) { + res.json(service ? { + bound_service: true + } : null); + }); + + function Node(config) { + var node = this; + RED.nodes.createNode(this, config); + + function setCredentials(msg) { + var creds = { + username : sUsername || node.credentials.username, + password : sPassword || node.credentials.password || config.password, + apikey : sApikey || node.credentials.apikey || config.apikey, + }; + + if (msg.params) { + if (msg.params.username) { + creds.username = msg.params.username; + } + if (msg.params.password) { + creds.password = msg.params.password; + } + if (msg.params.apikey) { + creds.apiKey = msg.params.apikey; + } + } + + return creds; + } + + function credentialCheck(u, p, k) { + if (!k && (!u || !p)) { + return Promise.reject('Missing Watson Assistant service credentials'); + } + return Promise.resolve(); + } + + function payloadCheck(msg) { + if (msg.payload && 'string' != typeof msg.payload) { + return Promise.reject('msg.payload must be either empty or a string'); + } + return Promise.resolve(); + } + + function idCheck(msg) { + if (!config.assistant_id && !(msg.params && msg.params.assistant_id)) { + return Promise.reject('Missing assistant_id. Check node documentation.'); + } + return Promise.resolve(); + } + + function setSessionID(msg) { + let session_id = null; + + if (!config.multisession) { + let id = node.context().flow.get('session_id'); + if (id) { + session_id = id; + } + } else if (msg.params && msg.params.session_id) { + session_id = msg.params.session_id; + } + + return session_id; + } + + function checkAndSet(source, target, field) { + if (source[field]) { + target[field] = source[field]; + } + } + + function setContext(msg, params) { + let context = null; + if (msg.params) { + checkAndSet(msg.params, params, 'context'); + } + return context; + } + + function setAdditionalContext(msg, params) { + if (msg.additional_context) { + params.context = params.context ? + params.context : + {'skills' : {'main skill' : {'user_defined': {}}}}; + + for (var prop in msg.additional_context) { + if (msg.additional_context.hasOwnProperty(prop)) { + params.context.skills['main skill']['user_defined'][prop] + = msg.additional_context[prop]; + } + } + } + } + + function setAssistantID(msg, params) { + checkAndSet(config, params, 'assistant_id'); + if (msg.params) { + checkAndSet(msg.params, params, 'assistant_id'); + } + } + + function setInputOptions(msg, params) { + // Setting the flags this way works as their default + // values are false. + ['alternate_intents', + 'return_context', + 'restart', + 'debug'].forEach((f) => { + checkAndSet(config, params.input.options, f); + if (msg.params) { + checkAndSet(msg.params, params.input.options, f); + } + }); + } + + function setParamInputs(msg, params) { + if (msg.params) { + ['intents', + 'entities'].forEach((f) => { + checkAndSet(msg.params, params, f); + }); + } + } + + function buildInputParams(msg) { + let params = { + 'input' : { + 'message_type': 'text', + 'text' : msg.payload, + 'options' : {} + }, + 'session_id' : setSessionID(msg) + }; + + let context = setContext(msg, params); + if (context) { + params.context = context; + } + + setAdditionalContext(msg, params); + setAssistantID(msg, params); + setInputOptions(msg, params); + setParamInputs(msg, params); + + return Promise.resolve(params); + } + + function setServiceSettings(msg, creds) { + const serviceSettings = { + headers: { + 'User-Agent': pkg.name + '-' + pkg.version + } + }; + + let endpoint = '', + optoutLearning = false, + version = SERVICE_VERSION; + + if (creds.apikey) { + serviceSettings.iam_apikey = creds.apikey; + } else { + serviceSettings.username = creds.username; + serviceSettings.password = creds.password; + } + + if (service) { + endpoint = service.url; + } + if (!config['default-endpoint'] && config['service-endpoint']) { + endpoint = config['service-endpoint']; + } + + if (config['optout-learning']){ + optoutLearning = true; + } + + if (config['timeout'] && config['timeout'] !== '0' && isFinite(config['timeout'])){ + serviceSettings.timeout = parseInt(config['timeout']); + } + + // Look for message overrides + if (msg.params) { + if (msg.params.endpoint) { + endpoint = msg.params.endpoint; + } + if (msg.params.version) { + version = msg.params.version; + } + if ((msg.params['optout_learning'])){ + optoutLearning = true; + } + if (msg.params.timeout !== '0' && isFinite(msg.params.timeout)){ + serviceSettings.timeout = parseInt(msg.params.timeout); + } + if (msg.params.disable_ssl_verification){ + serviceSettings.disable_ssl_verification = true; + } + } + + serviceSettings.version = version; + if (endpoint) { + serviceSettings.url = endpoint; + } + if (optoutLearning) { + serviceSettings.headers = serviceSettings.headers || {}; + serviceSettings.headers['X-Watson-Learning-Opt-Out'] = '1'; + } + + return Promise.resolve(serviceSettings); + } + + function buildService(settings) { + node.service = new AssistantV2(settings); + return Promise.resolve(); + } + + function checkSession(params) { + return new Promise(function resolver(resolve, reject){ + if (params.session_id) { + resolve(); + } else { + node.service.createSession({ + assistant_id: params.assistant_id + }, function(err, response) { + if (err) { + reject(err); + } else if (response && response.session_id) { + params.session_id = response.session_id; + if (!config.multisession) { + node.context().flow.set('session_id', params.session_id); + } + resolve(); + } else { + reject('Unable to set session'); + } + }); + } + }); + } + + function messageTurn(params) { + return new Promise(function resolver(resolve, reject){ + node.service.message(params, function(err, body) { + if (err) { + reject(err); + } else { + resolve(body); + } + }); + }); + } + + this.on('input', function(msg) { + var creds = setCredentials(msg), + params = {}; + + node.status({}); + + credentialCheck(creds.username, creds.password, creds.apikey) + .then(function(){ + return payloadCheck(msg); + }) + .then(function(){ + return idCheck(msg); + }) + .then(function(){ + return buildInputParams(msg); + }) + .then(function(p){ + params = p; + return setServiceSettings(msg, creds); + }) + .then(function(settings){ + return buildService(settings); + }) + .then(function(){ + return checkSession(params); + }) + .then(function(){ + node.status({ fill: 'blue', shape: 'dot', text: 'Calling Assistant service ...'}); + return messageTurn(params); + }) + .then(function(body){ + body.session_id = params.session_id; + msg.payload = body; + return Promise.resolve(); + }) + .then(function(){ + node.status({}); + node.send(msg); + }) + .catch(function(err){ + payloadutils.reportError(node,msg,err); + node.send(msg); + }); + + }); + } + + RED.nodes.registerType('watson-assistant-v2', Node, { + credentials: { + username: {type: 'text'}, + password: {type: 'password'}, + apikey: {type: 'password'} + } + }); +}; diff --git a/services/discovery/discovery-utils.js b/services/discovery/discovery-utils.js index 7965872c..b5b5ea3e 100644 --- a/services/discovery/discovery-utils.js +++ b/services/discovery/discovery-utils.js @@ -21,7 +21,7 @@ DiscoveryUtils.prototype = { buildService: function(username, password, apikey, endpoint) { let serviceSettings = { - version_date: '2018-08-01', + version_date: '2018-12-03', headers: { 'User-Agent': pkg.name + '-' + pkg.version } diff --git a/services/discovery/v1-document-loader.js b/services/discovery/v1-document-loader.js index ba14a79c..aa8fed5b 100644 --- a/services/discovery/v1-document-loader.js +++ b/services/discovery/v1-document-loader.js @@ -127,7 +127,7 @@ module.exports = function (RED) { function execute(params, msg, suffix) { var p = new Promise(function resolver(resolve, reject) { let discovery = discoveryutils.buildService(username, password, apikey, endpoint); - + // modify as getting addJsonDocument will be deprecated messages if ('.json' === suffix) { //method = 'addJsonDocument'; diff --git a/services/discovery/v1.html b/services/discovery/v1.html index 60624ecb..de6ad1c0 100644 --- a/services/discovery/v1.html +++ b/services/discovery/v1.html @@ -51,6 +51,7 @@ + + +
+ + +
+ - -
- - +
+ +
+