Java + SpringBootで個人ChatGPT作成方法紹介

# 使用環境
- OS：Win11
- Eclipse：Eclipse　2021
- Java：Java17
- SpringBoot：3.1.2
# openaiのAPI_KEY取得およびRequest/Responseの紹介
- https://qiita.com/brgd/items/d7bb6deb6492d2ad9b22 で詳しく説明しているので参照を...
# EclipseでSpringBootプロジェクト作成
- 当記事の内容範囲外ので割愛させてください、Googleしたら沢山ある。
## プロジェクト設定
#### ApplicationConfig設定
```ApplicationConfig.java
package com.chatgpt.config;

import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class ApplicationConfig {
	
	@Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder) {
        return builder.build();
    }

}

```



#### propertiesファイル作成 

```application.properties
openai.api.key=API_KEY
openai.api.url=https://api.openai.com/v1/chat/completions
```
## DTO作成
### RequestDTO作成
```Message.java
package com.chatgpt.domain.dto;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Message {
	public String role;
	public String content;
}
```

```ChatGptRequest.java
package com.chatgpt.domain.dto;

import java.util.List;

import lombok.Data;

@Data
public class ChatGptRequest {
	private String model;
	private List<Message> messages;
	private Integer max_tokens;

}


```

### ResponseDTO作成
```Choice.java
package com.chatgpt.domain.dto;

import lombok.Data;

@Data
public class Choice {
	public int index;
	public Message message;
	public String finish_reason;

}

```

```Usage.java
package com.chatgpt.domain.dto;

import lombok.Data;

@Data
public class Usage {
	public int prompt_tokens;
	public int completion_tokens;
	public int total_tokens;

}

```

```ChatGptResponse.java
package com.chatgpt.domain.dto;

import java.util.List;

import lombok.Data;

@Data
public class ChatGptResponse {
	public String id;
	public String object;
	public int created;
	public List<Choice> choices;
	public Usage usage;

}

```

## Service作成

```ChatGptService.java
package com.chatgpt.domain.seivice;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import com.chatgpt.domain.dto.ChatGptRequest;
import com.chatgpt.domain.dto.ChatGptResponse;
import com.chatgpt.domain.dto.Message;

@Service
public class ChatGptService {

	@Value("${openai.api.key}")
	private String apiKey;

	@Value("${openai.api.url}")
	private String apiUrl;

	@Autowired
	private RestTemplate restTemplate;

	public ChatGptResponse getChatCptResponse(String prompt) {

		HttpHeaders headers = new HttpHeaders();
		headers.setContentType(MediaType.APPLICATION_JSON);
		headers.set("Authorization", "Bearer " + apiKey);

		ChatGptRequest chatGptRequest = new ChatGptRequest();
		chatGptRequest.setModel("gpt-3.5-turbo"); //モデル名
		chatGptRequest.setMessages(List.of(new Message("system", prompt), new Message("user", prompt))); //メッセージ指定
		chatGptRequest.setMax_tokens(20); //一回で返す最大Token数

		HttpEntity<ChatGptRequest> request = new HttpEntity<>(chatGptRequest, headers);

		return restTemplate.postForObject(apiUrl, request, ChatGptResponse.class);
	}

}

```

## ControllerとView作成
```ChatGptWebController.java
package com.chatgpt.app.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

import com.chatgpt.app.form.ChatGptWebForm;
import com.chatgpt.domain.dto.ChatGptResponse;
import com.chatgpt.domain.seivice.ChatGptService;

@Controller
public class ChatGptWebController {

	@Autowired
	private ChatGptService chatGptService;

	@GetMapping("/")
	String index(Model model) {
		model.addAttribute("chatGptWebForm", new ChatGptWebForm("", ""));
		return "index";
	}

	@PostMapping(value = "/chatweb", params = "submit")
	public String searchAccount(Model model, @RequestParam("userInput") String userInput) {
		ChatGptResponse chatCPTResponse = chatGptService.getChatCptResponse(userInput);
		ChatGptWebForm chatGptWebForm = new ChatGptWebForm();
		chatGptWebForm.setUserInput(userInput);
		chatGptWebForm.setMessage(chatCPTResponse.getChoices().get(0).getMessage().getContent());
		model.addAttribute("chatGptWebForm", chatGptWebForm);
		return "index";
	}

}

```

```ChatGptWebForm.java

```

```index.html
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Title</title>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport"
	content="width=device-width, initial-scale=1, shrink-to-fit=no">

<!-- Bootstrap CSS -->
<link rel="stylesheet"
	href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
	integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"
	crossorigin="anonymous">
</head>
<body>
	<form action="/chatweb" th:action="@{/chatweb}" method="post"
		th:object="${chatGptWebForm}" class= "ml-3">
		<div class="form-group row mt-3">
			<label for="userInput" class="col-sm-2 col-form-label">質問</label>
			<div class="col-sm-9">
				<input type="text" class="form-control" th:field="*{userInput}">
			</div>
		</div>
		<div class="form-group row">
			<div class="col-sm-10">
				<button type="submit" name="submit" class="btn btn-primary">送信</button>
			</div>
		</div>



		<div class="col-sm-11">
			<h3>回答:</h3>
		<!-- <p class="bg-secondary" th:text="*{userInput}"></p> -->
			<p class="bg-info" th:text="*{message}"></p>
		</div>
	</form>

	<!-- Optional JavaScript -->
	<!-- jQuery first, then Popper.js, then Bootstrap JS -->
	<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
		integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
		crossorigin="anonymous"></script>
	<script
		src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"
		integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1"
		crossorigin="anonymous"></script>
	<script
		src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"
		integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM"
		crossorigin="anonymous"></script>
</body>
</html>
```

# 結果を確認

- 初期画面
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148710/e6984bb3-5a6e-f56f-2be1-4d503b9b023a.png)
- 質問してみる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148710/7d55634b-60a6-d15e-5ea0-3d552f5a07a7.png)

# その他
- 上記まではChatGPTのWEBAPP作ったんですがもちろん個人APIとして作成することも可能です。
### 個人APIとして作成
　- webのControllerをRestApiのControllerとして作成する
　- ApiのResponseを`ResponseEntity`にして返す。

```ChatGptApiController.java
package com.chatgpt.app.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.chatgpt.domain.dto.ChatGptResponse;
import com.chatgpt.domain.seivice.ChatGptService;

@RestController
public class ChatGptApiController {

	@Autowired
	private ChatGptService chatGptService;

	@PostMapping("/chatapi")
	public ResponseEntity<ChatGptResponse> processInputRequest(@RequestBody("message") String userInput) {
		ChatGptResponse chatCPTResponse = chatGptService.getChatCptResponse(userInput);
		return new ResponseEntity<>(chatCPTResponse, HttpStatus.OK);
	}

}


```

### Apiを呼んでみる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148710/ec2e330f-96a4-30ed-279f-d320ddc33a9c.png)