-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* [FEATURE] 챗봇 엔티티 생성 * [FEATURE] 메시지 전송 dto 구현 * [FEATURE] 메시지 응답 dto 구현 * [FEATURE] repository 구현 * [FEATURE] 세션 만드는 createSession, 메시지 전송하고 응답 받는 createMessage 메소드 구현 * [FEATURE] OpenAI API와 통신하면서 세션을 만들거나, 메시지에 대한 응답을 받는 메소드들 구현 * [STYLE] 코드 포맷팅
- Loading branch information
Showing
6 changed files
with
302 additions
and
0 deletions.
There are no files selected for viewing
42 changes: 42 additions & 0 deletions
42
src/main/java/com/fav/daengnyang/domain/chatbot/controller/ChatbotController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package com.fav.daengnyang.domain.chatbot.controller; | ||
|
||
import com.fav.daengnyang.domain.chatbot.service.ChatbotService; | ||
import com.fav.daengnyang.domain.chatbot.service.dto.request.CreateMessageRequest; | ||
import com.fav.daengnyang.domain.chatbot.service.dto.response.MessageResponse; | ||
import com.fav.daengnyang.global.auth.dto.MemberPrincipal; | ||
import com.fav.daengnyang.global.dto.response.ExceptionResponse; | ||
import com.fav.daengnyang.global.web.dto.response.SuccessResponse; | ||
import jakarta.validation.Valid; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.security.core.annotation.AuthenticationPrincipal; | ||
import org.springframework.web.bind.annotation.PostMapping; | ||
import org.springframework.web.bind.annotation.RequestBody; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
@RequiredArgsConstructor | ||
@RequestMapping("/chatbots") | ||
@RestController | ||
public class ChatbotController { | ||
|
||
private final ChatbotService chatbotService; | ||
|
||
// 챗봇에게 메시지 보내고 응답 받기 | ||
@PostMapping | ||
public SuccessResponse<?> createMessage( | ||
@AuthenticationPrincipal MemberPrincipal memberPrincipal, | ||
@Valid @RequestBody CreateMessageRequest messageRequest | ||
) throws InterruptedException { | ||
MessageResponse response = chatbotService.getMessage(memberPrincipal.getMemberId(), messageRequest); | ||
return SuccessResponse.ok(response); | ||
} | ||
|
||
// 챗봇에게 처음 메시지 보내기(새로운 세션 만들기) | ||
@PostMapping("/session") | ||
public SuccessResponse<?> createSession( | ||
@AuthenticationPrincipal MemberPrincipal memberPrincipal | ||
){ | ||
chatbotService.createSession(memberPrincipal.getMemberId()); | ||
return SuccessResponse.created("세션 생성에 성공했습니다."); | ||
} | ||
} |
28 changes: 28 additions & 0 deletions
28
src/main/java/com/fav/daengnyang/domain/chatbot/entity/Chatbot.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package com.fav.daengnyang.domain.chatbot.entity; | ||
|
||
import jakarta.persistence.*; | ||
import lombok.Builder; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
import lombok.Setter; | ||
|
||
@Entity | ||
@Table(name = "chatbot") | ||
@Getter | ||
@Setter | ||
@NoArgsConstructor | ||
public class Chatbot { | ||
|
||
@Id | ||
@Column(name = "member_id") | ||
private Long memberId; | ||
|
||
@Column(name = "thread_id") | ||
private String threadId; | ||
|
||
@Builder | ||
public Chatbot(Long memberId, String threadId) { | ||
this.memberId = memberId; | ||
this.threadId = threadId; | ||
} | ||
} |
9 changes: 9 additions & 0 deletions
9
src/main/java/com/fav/daengnyang/domain/chatbot/repository/ChatbotRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.fav.daengnyang.domain.chatbot.repository; | ||
|
||
import com.fav.daengnyang.domain.chatbot.entity.Chatbot; | ||
import org.springframework.data.jpa.repository.JpaRepository; | ||
import org.springframework.stereotype.Repository; | ||
|
||
@Repository | ||
public interface ChatbotRepository extends JpaRepository<Chatbot, Long> { | ||
} |
190 changes: 190 additions & 0 deletions
190
src/main/java/com/fav/daengnyang/domain/chatbot/service/ChatbotService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,190 @@ | ||
package com.fav.daengnyang.domain.chatbot.service; | ||
|
||
import com.fasterxml.jackson.databind.JsonNode; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import com.fav.daengnyang.domain.chatbot.entity.Chatbot; | ||
import com.fav.daengnyang.domain.chatbot.repository.ChatbotRepository; | ||
import com.fav.daengnyang.domain.chatbot.service.dto.request.CreateMessageRequest; | ||
import com.fav.daengnyang.domain.chatbot.service.dto.response.MessageResponse; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.http.HttpEntity; | ||
import org.springframework.http.HttpHeaders; | ||
import org.springframework.http.HttpMethod; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
import org.springframework.web.client.RestTemplate; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
@Transactional | ||
public class ChatbotService { | ||
|
||
private final ChatbotRepository chatbotRepository; | ||
|
||
// GPT API 키 | ||
@Value("${gpt-key}") | ||
private String gptKey; | ||
|
||
// assistant ID | ||
@Value(("${assistant-id}")) | ||
private String assistantId; | ||
|
||
// 유저의 메시지에 대한 응답 받기 | ||
public MessageResponse getMessage(Long memberId, CreateMessageRequest messageRequest) throws InterruptedException { | ||
String threadId = getThreadId(memberId); | ||
sendMessage(threadId, messageRequest.getChat()); | ||
runMessages(threadId, assistantId); | ||
Thread.sleep(1500); | ||
List<String> messages = getMessageTextValues(threadId); | ||
MessageResponse response = MessageResponse.builder() | ||
.chat(messages.get(0)) | ||
.build(); | ||
return response; | ||
} | ||
|
||
// 세션 생성하면서 첫 메시지 보내는 메소드 | ||
public void createSession(Long memberId) { | ||
chatbotRepository.deleteById(memberId); | ||
getThreadId(memberId); | ||
} | ||
|
||
// 지금 스레드(세션) id 가져오기 | ||
private String getThreadId(Long memberId) { | ||
try { | ||
Chatbot chatbot = chatbotRepository.findById(memberId).orElseThrow(Exception::new); | ||
return chatbot.getThreadId(); | ||
} catch (Exception e){ | ||
String thread = createThreadId(); | ||
Chatbot chatbot = new Chatbot(); | ||
chatbot.setThreadId(thread); | ||
chatbot.setMemberId(memberId); | ||
chatbotRepository.save(chatbot); | ||
return thread; | ||
} | ||
} | ||
|
||
// 새로운 스레드(세션 만들기) | ||
private String createThreadId() { | ||
String URL = "https://api.openai.com/v1/threads"; | ||
try { | ||
// HTTP 요청 설정 | ||
RestTemplate restTemplate = new RestTemplate(); | ||
HttpHeaders headers = new HttpHeaders(); | ||
headers.set("Content-Type", "application/json"); | ||
headers.set("Authorization", "Bearer " + gptKey); | ||
headers.set("OpenAI-Beta", "assistants=v2"); | ||
HttpEntity<String> entity = new HttpEntity<>("", headers); | ||
|
||
// API 호출 | ||
ResponseEntity<String> response = restTemplate.exchange(URL, HttpMethod.POST, entity, String.class); | ||
String jsonResponse = response.getBody(); | ||
|
||
// JSON 응답에서 id 추출 | ||
ObjectMapper objectMapper = new ObjectMapper(); | ||
JsonNode rootNode = objectMapper.readTree(jsonResponse); | ||
return rootNode.path("id").asText(); | ||
} catch (Exception e) { | ||
e.printStackTrace(); | ||
return null; | ||
} | ||
} | ||
|
||
// 메시지 전송하기 | ||
private void sendMessage(String threadId, String messageContent) { | ||
String BASE_URL = "https://api.openai.com/v1/threads/"; | ||
try { | ||
// HTTP 요청 설정 | ||
RestTemplate restTemplate = new RestTemplate(); | ||
HttpHeaders headers = new HttpHeaders(); | ||
headers.set("Content-Type", "application/json"); | ||
headers.set("Authorization", "Bearer " + gptKey); | ||
headers.set("OpenAI-Beta", "assistants=v2"); | ||
|
||
// 요청 바디 설정 | ||
String requestBody = String.format( | ||
"{\"role\": \"user\", \"content\": \"%s\"}", | ||
messageContent | ||
); | ||
HttpEntity<String> entity = new HttpEntity<>(requestBody, headers); | ||
|
||
// API 호출 | ||
String url = BASE_URL + threadId + "/messages"; | ||
restTemplate.exchange(url, HttpMethod.POST, entity, String.class); | ||
} catch (Exception e) { | ||
e.printStackTrace(); | ||
return; | ||
} | ||
} | ||
|
||
// 메시지를 GPT에서 동작시킴 | ||
private void runMessages(String threadId, String assistantId) { | ||
String BASE_URL = "https://api.openai.com/v1/threads/"; | ||
try { | ||
// HTTP 요청 설정 | ||
RestTemplate restTemplate = new RestTemplate(); | ||
HttpHeaders headers = new HttpHeaders(); | ||
headers.set("Authorization", "Bearer " + gptKey); | ||
headers.set("Content-Type", "application/json"); | ||
headers.set("OpenAI-Beta", "assistants=v2"); | ||
|
||
// 요청 바디 설정 | ||
String requestBody = String.format( | ||
"{\"assistant_id\": \"%s\"}", | ||
assistantId | ||
); | ||
HttpEntity<String> entity = new HttpEntity<>(requestBody, headers); | ||
|
||
// API 호출 | ||
String url = BASE_URL + threadId + "/runs"; | ||
restTemplate.exchange(url, HttpMethod.POST, entity, String.class); | ||
} catch (Exception e) { | ||
e.printStackTrace(); | ||
return; | ||
} | ||
} | ||
|
||
// Message의 Text를 받아서 반환하는 메소드 | ||
private List<String> getMessageTextValues(String threadId) { | ||
String BASE_URL = "https://api.openai.com/v1/threads/"; | ||
try { | ||
// HTTP 요청 설정 | ||
RestTemplate restTemplate = new RestTemplate(); | ||
HttpHeaders headers = new HttpHeaders(); | ||
headers.set("Content-Type", "application/json"); | ||
headers.set("Authorization", "Bearer " + gptKey); | ||
headers.set("OpenAI-Beta", "assistants=v2"); | ||
|
||
HttpEntity<String> entity = new HttpEntity<>(null, headers); | ||
|
||
// API 호출 | ||
String url = BASE_URL + threadId + "/messages"; | ||
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, entity, String.class); | ||
|
||
// 응답 처리 | ||
String jsonResponse = response.getBody(); | ||
ObjectMapper objectMapper = new ObjectMapper(); | ||
JsonNode rootNode = objectMapper.readTree(jsonResponse); | ||
List<String> textValues = new ArrayList<>(); | ||
|
||
// 메시지에서 text value 추출 | ||
for (JsonNode messageNode : rootNode.path("data")) { | ||
for (JsonNode contentNode : messageNode.path("content")) { | ||
if (contentNode.path("type").asText().equals("text")) { | ||
String textValue = contentNode.path("text").path("value").asText(); | ||
textValues.add(textValue); | ||
} | ||
} | ||
} | ||
|
||
return textValues; | ||
} catch (Exception e) { | ||
e.printStackTrace(); | ||
return null; | ||
} | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
...main/java/com/fav/daengnyang/domain/chatbot/service/dto/request/CreateMessageRequest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package com.fav.daengnyang.domain.chatbot.service.dto.request; | ||
|
||
import jakarta.validation.constraints.NotNull; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
import lombok.Setter; | ||
|
||
@Getter | ||
@Setter | ||
@NoArgsConstructor | ||
public class CreateMessageRequest { | ||
|
||
@NotNull(message = "챗봇에게 보낼 메시지를 입력해 주세요.") | ||
private String chat; | ||
} |
18 changes: 18 additions & 0 deletions
18
src/main/java/com/fav/daengnyang/domain/chatbot/service/dto/response/MessageResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package com.fav.daengnyang.domain.chatbot.service.dto.response; | ||
|
||
import lombok.Builder; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
import lombok.Setter; | ||
|
||
@NoArgsConstructor | ||
@Getter | ||
@Setter | ||
public class MessageResponse { | ||
private String chat; | ||
|
||
@Builder | ||
public MessageResponse(String chat) { | ||
this.chat = chat; | ||
} | ||
} |