watsonxのAPIをJavaから呼び出してみる (Llama 2) _V2

# コードをコンパクトにする

前記事では「watsonxのAPIをJavaから呼び出してみる」として単一クラスでAPI呼び出しを行っていました。

https://qiita.com/oyahiroki/items/cd6d42d6695d498a934d

このコードはApache HttpClient を使っていることもあって、ちょっと長ったらしいです。

# HttpPostBuilder

そこで以下のようなクラスを作成し、簡単にHTTPリクエストを送信できるように工夫します。（本コードはJava8でも動作させる目的でJava11から登場するHttpClientを使っていません）

```java
package com.ibm.util;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.entity.UrlEncodedFormEntity;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.NameValuePair;
import org.apache.hc.core5.http.io.entity.StringEntity;
import org.apache.hc.core5.http.message.BasicHeader;
import org.apache.hc.core5.http.message.BasicNameValuePair;
public class HttpPostBuilder {
	private HttpPost httpPost;
	private String data = null;
	private List<NameValuePair> params = new ArrayList<>();
	public HttpPostBuilder(String end_point) throws URISyntaxException {
		this.httpPost = new HttpPost(new URI(end_point));
	}
	public HttpPostBuilder data(String data) {
		this.data = data;
		return this;
	}
	public HttpPostBuilder H(String key, String value) {
		this.httpPost.setHeader(new BasicHeader(key, value));
		return this;
	}
	public HttpPostBuilder D(String key, String value) {
		this.params.add(new BasicNameValuePair(key, value));
		return this;
	}
	public HttpPost build() {
		if (data != null) {
			StringEntity requestEntity = new StringEntity(data,
					ContentType.APPLICATION_JSON);
			this.httpPost.setEntity(requestEntity);
		} //
		else if (params.size() > 0) {
			httpPost.setEntity((new UrlEncodedFormEntity(params)));
		}
		return httpPost;
	}
}
```

# 改良版

```java
package com.ibm.watsonx;

import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.entity.UrlEncodedFormEntity;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.NameValuePair;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.io.entity.StringEntity;
import org.apache.hc.core5.http.message.BasicHeader;
import org.apache.hc.core5.http.message.BasicNameValuePair;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.ibm.util.HttpPostBuilder;
public class HelloWatsonX2 {
	public static void main(String[] args) throws Exception {
		// 「watsonx > プロンプト・ラボ > コードの表示」から取得する
		String project_id = System.getProperty("project_id", null);
		// 「IBM Cloud > 管理 > アクセス(IAM) > サービスID」から取得する
		String apikey = System.getProperty("apikey", null);
		// 利用したいLLM
		String model_id = "meta-llama/llama-2-70b-chat";
		// 入力文
		String input = "入力:\\n日本で一番高い山の名前は？\\n\\n出力:\\n";
		String end_point = "https://us-south.ml.cloud.ibm.com/ml/v1-beta/generation/text?version=2023-05-29";
		// APIキーを利用して取得できるアクセストークン (１時間有効)
		String access_token = null;
		{ // APIキーを利用してアクセストークンを取得する(１時間有効)
			String end_point_token = "https://iam.cloud.ibm.com/identity/token";
			try (CloseableHttpClient httpClient = HttpClients.createDefault();) {
				HttpPost httpPost = (new HttpPostBuilder(end_point_token)) //
						.H("Content-Type", "application/x-www-form-urlencoded") //
						.H("Accept", "application/json") //
						.D("grant_type","urn:ibm:params:oauth:grant-type:apikey") //
						.D("apikey", apikey) //
						.build();
				// Execute the request
				CloseableHttpResponse response_token = httpClient.execute(httpPost);
				try {
					// Get the response entity
					HttpEntity responseEntity = response_token.getEntity();
					if (responseEntity != null) {
						// Convert the response entity to a string
						String responseString = EntityUtils.toString(responseEntity, StandardCharsets.UTF_8);
						{
							JsonObject jo = (new Gson()).fromJson(responseString, JsonObject.class);
							System.out.println(jo.toString());
							// 取得したアクセストークンを変数にセットする
							access_token = jo.get("access_token").getAsString();
						}
					}
					EntityUtils.consume(responseEntity);
				} finally {
					response_token.close();
				}
			}
		}

		try (CloseableHttpClient httpClient = HttpClients.createDefault();) {
			JsonObject request_body = new JsonObject();
			{
				request_body.addProperty("model_id", model_id);
				request_body.addProperty("input", input);
				JsonObject parameters = new JsonObject();
				{
					parameters.addProperty("decoding_method", "greedy");
					parameters.addProperty("max_new_tokens", 20);
					parameters.addProperty("min_new_tokens", 10);
					parameters.add("stop_sequences", new JsonArray());
					parameters.addProperty("repetition_penalty", 1);
				}
				request_body.add("request_body", parameters);
				request_body.addProperty("project_id", project_id);
			}
			HttpPost httpPost = (new HttpPostBuilder(end_point)) //
					.H("Content-Type", "application/json") //
					.H("Accept", "application/json") //
					.H("Authorization", "Bearer " + access_token) //
					.data(request_body.toString()) //
					.build();
			// Execute the request
			CloseableHttpResponse response = httpClient.execute(httpPost);
			try {
				// Get the response entity
				HttpEntity responseEntity = response.getEntity();
				if (responseEntity != null) {
					// Convert the response entity to a string
					String responseString = EntityUtils.toString(responseEntity,StandardCharsets.UTF_8);
					JsonObject jo = (new Gson()).fromJson(responseString,JsonObject.class);
					System.out.println(jo.get("results").getAsJsonArray().get(0)
							.getAsJsonObject().get("generated_text")
							.getAsString());
				}
				EntityUtils.consume(responseEntity);
			} finally {
				response.close();
			}
		} catch (URISyntaxException e) {
			e.printStackTrace();
		}
	}
}
```

# 結果

コードが少し短くなりました！

入力した質問「日本で一番高い山の名前は？」
返ってきた回答「日本で一番高い山の名前は、富士山です。」







以上。