LoginSignup
4
1

watsonxのAPIをNode.jsから呼び出してみる

Posted at

はじめに

前回の記事「watsonxのAPIを呼び出せるようになるまで」では、 curl コマンドを利用してwatsonxのAPIを呼び出すことを紹介しましたが、今回の記事では Node.js から呼び出す方法を紹介します。
本当はNode.js用のSDKが公開されていれば良いのですが、2023/8/3時点ではPythonしか無いので、Node使いとしてはSDK無しでの実装が必要です。

実行環境

  • node: v18.17.0
  • npm: 9.6.7
  • os: windows 11

コード紹介

とりあえずコード全文。
Node.jsのv18から fetch API が素で使えるようになったので、特にpackageにモジュールを追加していません。
ただ、v17以前の環境で動かす場合、 node-fetch のモジュールで置き換えても動作することは確認済です。

index.js
(async() => {
  const WML_APIKEY = `前回の記事で作成したAPIキー`;
  const PROJECT_ID = 'watsonxのProject ID';
  const iamEndpointUrl = 'https://iam.cloud.ibm.com/identity/token';
  const iamRequestUrl = `${iamEndpointUrl}?grant_type=urn:ibm:params:oauth:grant-type:apikey&apikey=${WML_APIKEY}`
  const options = {
    method: 'POST',
    headers: { 
      "Content-Type": "application/x-www-form-urlencoded",
      "Accept": "application/json",
    },
  }
  try {
    const iamResponse = await fetch(iamRequestUrl, options);
    const iamJson = await iamResponse.json();
    if(iamJson.access_token){
      
      const accessToken = iamJson.access_token;
      const watsonxEndpointUrl = 'https://us-south.ml.cloud.ibm.com/ml/v1-beta/generation/text?version=2023-05-29';
      const watsonxBody = {
        model_id: "ibm/mpt-7b-instruct2",
        input: "入力:\\n日本の首都は?",
        parameters: {
          decoding_method: "greedy",
          max_new_tokens: 20,
          min_new_tokens: 0,
          stop_sequences: [],
          repetition_penalty: 1
        },
        project_id: PROJECT_ID
      }
      const watsonxOptions = {
        method: 'POST',
        headers: { 
          'Content-Type': 'application/json',
          'Accept': 'application/json',
          'Authorization': `Bearer ${accessToken}`
        },
        body: JSON.stringify(watsonxBody),
      }
      const watsonxResponse = await fetch(watsonxEndpointUrl, watsonxOptions);
      const watsonxJson = await watsonxResponse.json();
      console.log(watsonxJson);
    }
  } catch (error) {
    console.log(error);
  }
})();

実行結果は、以下のような内容です。

C:\node-apps\watsonx-test> node index.js
{
  model_id: 'ibm/mpt-7b-instruct2',
  created_at: '2023-08-03T01:28:10.364Z',
  results: [
    {
      generated_text: '\\n東京です。\\n東京は日本の首都',
      generated_token_count: 20,
      input_token_count: 11,
      stop_reason: 'MAX_TOKENS'
    }
  ]
}

何か、、、くどい回答ですね。

詰まったところ

当初は何度やっても以下のようなエラーが返ってきて、APIに渡してるJSONの先頭文字に o なんて無いのにと悩んでいました。

{
  errors: [
    {
      code: 'json_unmarshal_failed',
      message: "Failed to deserialize json: invalid character 'o' looking for beginning of value"
    }
  ],
  trace: '90726e661564ae478594ef92f64b5d88',
  status_code: 400
}

普段はSDKを用いてサービスのAPIを利用することに慣れているので、下記のようにAPIに渡したい内容を記述しがちです。

const watsonxBody = {
  model_id: "ibm/mpt-7b-instruct2",
  input: "入力:\\n日本の首都は?",
  parameters: {
    decoding_method: "greedy",
    max_new_tokens: 20,
    min_new_tokens: 0,
    stop_sequences: [],
    repetition_penalty: 1
  },
  project_id: PROJECT_ID
}

でも、これってよく考えるとJSONじゃなくてObjectですよね。
なのでfetchに何も考えずに渡すと [object object] になってしまってるので、先頭文字のoは認識できないよってエラーになってたようです。気づくのに半日以上かけてしまいました。。。orz
なので、fetchにbodyを渡す際には JSON.stringify() を利用して文字列にすることで、エラーが解消しました。

body: JSON.stringify(watsonxBody),

注意点

今回紹介したコードでは下記のように、都度IAMからアクセストークンを取得しています。

const iamResponse = await fetch(iamRequestUrl, options);
const iamJson = await iamResponse.json();

しかし、アクセストークンはデフォルトでは1時間再利用可能となっているので、都度発行するのは本来正しくありません。

常駐アプリケーションに組み込む際は、アクセストークンを発行してから1時間以内に再利用する場合は、既存のアクセストークンを流用するようにコードを書くのが良いです。
やり方はいくつかあると思いますが、例えば、アクセストークンを取得する関数を別で定義して、アクセストークンを取得した時に、そのアクセストークンとそのプログラムが稼働するプラットフォームの時刻情報を記録して、再度その関数が呼び出された時にプラットフォームの時刻を取得して、記録された時刻と1時間以内の差異なら記録されたアクセストークンを取得する、1時間以上あるならば再発行といったコードを書くのも1つの手かと思います。

さいごに

いかがでしたでしょうか?
Python使いの人も多いと思いますが、私のようにNodeばかり使ってる人でも、watsonxをプログラムから呼び出せることが証明できました。
ちょっとアプリから呼び出せるようにしてみようかな、とか、少しでもwatonsxの興味が広がれば幸いです。

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1