小ネタ: Watson SDK V4(Node.js)では、もっと楽にコールバック(やPromise)を意識せずAPIを呼び出せるようになりました


# 要は(TL;DR;)
- 2019/3月にWatson SDKが新しくなりました（V4)
- 新しいWatson SDKのnode-js版ではSDK APIの呼び出し時に**デフォルトでPromiseを返してくれる**ようになりました。よって、ご自身のコードでPromiseを扱う必要がなくなりました。
- 具体的には**async**/**await**の枠組みでWatson SDKのAPIを呼び出せば、簡単に逐次的な処理を書けます


# はじめに
こんにちわ！石田です。2018/11月に「[小ネタ - Watson API / Node.jsでコールバック(やPromise)を意識せずAPIを逐次実行するコードを書く方法](https://qiita.com/ishida330/items/2968b3a7fa49fd62fc37)」という記事でpromisifyを使って逐次的なコードを書く方法をご紹介したのですが、Watson SDKが新しくなり更に楽に書けるようになりました。

新SDK紹介のブログ記事 :newspaper: [IBM Watson SDK Major Releases March 2019](https://medium.com/ibm-watson/ibm-watson-sdk-major-releases-march-2019-e57c8a66201e)によると
>The major new feature is support for Promises — every call returns a Promise if no callback is passed in. If your code had to wrap the call in a Promise, you don’t need to anymore! The call returns a Promise by default.

翻訳
> 主要な新機能としてPromiseがサポートされました。(SDKは)コールバック関数を省略してAPIを呼び出(Call)せば、毎回Promiseを返してくれます。従来、皆さんがプログラムのコードをPrimiseで包み込むように書いていたのなら、もうその必要はありません！APIの呼び出し(Call)はデフォルトでPromiseを返してくれます。

ということです。
よって、以下のようにjavascriptのasync/await構文と組み合わせると、こんな感じで直感的なコードが書けます。

```js:assistantの例
async function hoge(parm) {
    ...
    let response = await assistant.message(assistant_params); // SDKはPromiseを返す
    [直下に続けてresponseの処理を書ける]
    ...
}    
```
:point_up: **aync/await**については@soarflatさんの記事:newspaper: 「[async/await 入門（JavaScript）](https://qiita.com/soarflat/items/1a9613e023200bbebcb3)」が参考になります。

# AssistantとDiscoveryの併用の例

[以前の記事](https://qiita.com/ishida330/items/2968b3a7fa49fd62fc37)では「Assistantに問いかけて確信度が基準値を下回ったらDiscoveryに改めて問いかける」シナリオの簡単なサンプルをお示ししましたが、下記が新しいSDKを使った場合の例です。さらに簡単に[^1]なっているのがお分かりいただけるかと思います。

[^1]: 今回はWatsonAssistantのV2のAPIを使うようにしたので、Sessionの扱いの部分が前より長くなってますが、そこは本題と関係ないのでお忘れください。:-)

<details><summary>新SDKでの例</summary><div>

```js
'use strict';
/*
    Watson Assistant　& Watson Discoveryをpromisifyを使って逐次実行
*/
/* ibmcloud上のnode.jsで実行するならここをコメントアウト
const express = require('express');
const app = express();
const port = process.env.PORT || 8080;
app.get('/', (req, res) => res.send('Hello World!'));
app.listen(port, () => console.log('Example app listening on port %d', port));
*/

//    コンスタント
const MIN_CONFIDENCE = 0.6; //　求める確信度。これを下回ったらDiscoveryに聞きなおす
const ASSISTANT_ID = "3462b323-5254-4b1e-895c-XXXXXXXXXXXX";
const DISCOVERY_ENVIRONMENT_ID = "957c7650-c919-435a-XXXXXXXXXXXXXXXXXX";
const DISCOVERY_COLLECTION_ID = "a1d33db4-c6f1-4ee1-XXXXXXXXXXXXXXX";

// Watson Assistant V2
// 別の記事に書きましたが最新のWatson SDKでは認証情報(Credentials)を
// ファイル（ibm-credentials.env）に外出しできますので、その前提で
const AssistantV2 = require('ibm-watson/assistant/v2');
const assistant = new AssistantV2({
    version: '2019-02-28',
});

let assistant_params ={
      assistant_id: ASSISTANT_ID,
      session_id: null, 
      input: {
        'message_type': 'text',
        'text': null
        }
}

// Watson Discovery
const DiscoveryV1 = require('ibm-watson/discovery/v1');
const discovery = new DiscoveryV1({
    version: '2019-03-25'
});

let discovery_params = {
    environment_id: DISCOVERY_ENVIRONMENT_ID,
    collection_id:  DISCOVERY_COLLECTION_ID,
    passages: true,
    passages_count: 1,
    natural_language_query: null
};


/*
    Assiatant V2 Session作成
*/
async function createSession(assistantID) {
   return await assistant.createSession({
                assistant_id: assistantID
            }); 
}

async function chat(session,text) {
    console.log('--------------------------------------------------------------------------')
    console.log("chat:session[" + JSON.stringify(session) +"] 発話内容:[" + text +"]");

    assistant_params.session_id = session["session_id"];
    assistant_params.input.text = text;

    let response = await assistant.message(assistant_params);

    //　Assistantの回答の確信度が所定の基準を下回ったらDiscoveryにて再度検索
    if (response.output.intents.length > 0){
        console.log('Assistantの回答確信度:'　+ response.output.intents[0].confidence )
        if (response.output.intents[0].confidence < MIN_CONFIDENCE ){
            console.log('--> Assistantの回答確信度が低いのでDiscoveryを呼び出します')
            discovery_params.natural_language_query = text;
            response = await discovery.query(discovery_params);
            console.log('Discoveryからの回答:',
                JSON.stringify(response.passages, null, 2));        
        } else {
            //　Assistantの回答確信度が所定の基準以上ならAssistantの回答を提示
            console.log('Assistantからの応答:',
                JSON.stringify(response.output.generic[0].text, null, 2));
        }
    }    
}

// 発話を順番に逐次的に呼び出す
async function dialog(){
    // Watson Assistan V2 = まずSessionを作る
    let session = await createSession(ASSISTANT_ID);
    // 以降の発話ではセッションを持ちまわる
    let res = await chat(session, "");
        res = await chat(session, "こんにちわ");
        res = await chat(session, "Excelでページレイアウトビューを表示する方法を教えてください");
}

dialog();
```
</div></details>


<details><summary>実行ログ</summary><div>

```text
--------------------------------------------------------------------------
chat:session[{"session_id":"649ebc10-40cf-4b4b-b547-eb834eefca31"}] 発話内容:[]
--------------------------------------------------------------------------
chat:session[{"session_id":"649ebc10-40cf-4b4b-b547-eb834eefca31"}] 発話内容:[こんにちわ]
Assistantの回答確信度:1
Assistantからの応答: "いらっしゃいませ。ご相談内容をお聞かせくださいますか？"
--------------------------------------------------------------------------
chat:session[{"session_id":"649ebc10-40cf-4b4b-b547-eb834eefca31"}] 発話内容:[Excelでページレイアウトビューを表示する方法を教えて
ください]
Assistantの回答確信度:0.3350169897079468
--> Assistantの回答確信度が低いのでDiscoveryを呼び出します
Discoveryからの回答: [
  {
    "document_id": "842b0690418b3d6d4c43203d2a511166",
    "passage_score": 38.83379280996439,
    "passage_text": "</p>\n\n<p><img alt=\"\" src=\"http://pc-support.com/qasearch/1007/doc/answer/image/mso2007/e162-1.gif\"/><br/>\nページレイアウトビューを表示するには、［表示］タブにある［ブックの表示］グループの［ページレイアウトビュー］ボタンをクリックし
ます。</p>\n<div class=\"ca\"></div>\n\n</td>\n</tr>\n</tbody></table>\n   </div></div><div class=\"qanda-block-more\">\n   <div c
lass=\"qanda",
    "start_offset": 4699,
    "end_offset": 4995,
    "field": "html"
  }
]
```
</div></details>

以上です。