-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #37 from 9oormthon-univ/dev
GPT for Summit
- Loading branch information
Showing
14 changed files
with
505 additions
and
0 deletions.
There are no files selected for viewing
30 changes: 30 additions & 0 deletions
30
src/main/java/univ/yesummit/domain/gpt/config/ChatGPTConfig.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,30 @@ | ||
package univ.yesummit.domain.gpt.config; | ||
|
||
import lombok.Getter; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.http.HttpHeaders; | ||
import org.springframework.http.MediaType; | ||
import org.springframework.web.client.RestTemplate; | ||
|
||
@Configuration | ||
public class ChatGPTConfig { | ||
|
||
@Value("${openai.secret-key}") | ||
private String secretKey; | ||
|
||
|
||
@Bean | ||
public RestTemplate restTemplate() { | ||
return new RestTemplate(); | ||
} | ||
|
||
@Bean | ||
public HttpHeaders httpHeaders() { | ||
HttpHeaders headers = new HttpHeaders(); | ||
headers.setBearerAuth(secretKey); | ||
headers.setContentType(MediaType.APPLICATION_JSON); | ||
return headers; | ||
} | ||
} |
59 changes: 59 additions & 0 deletions
59
src/main/java/univ/yesummit/domain/gpt/controller/ChatGPTController.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,59 @@ | ||
package univ.yesummit.domain.gpt.controller; | ||
|
||
|
||
import io.swagger.v3.oas.annotations.Operation; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.web.bind.annotation.*; | ||
import univ.yesummit.domain.gpt.dto.ChatCompletionDTO; | ||
import univ.yesummit.domain.gpt.service.ChatGPTService; | ||
import univ.yesummit.global.exception.dto.ResponseVO; | ||
import univ.yesummit.global.resolver.LoginUser; | ||
import univ.yesummit.global.resolver.User; | ||
|
||
import java.util.Map; | ||
|
||
@Slf4j | ||
@RestController | ||
@RequestMapping(value = "/v1/api/gpt") | ||
@RequiredArgsConstructor | ||
public class ChatGPTController { | ||
|
||
private final ChatGPTService chatGPTService; | ||
|
||
|
||
/** | ||
* ChatGPT 유효한 모델인지 조회 | ||
*/ | ||
@GetMapping("/model") | ||
@Operation(summary = "유효한 모델인지 판단", description = "Chat GPT의 유효한 모델인지 조회하고, 판단합니다.") | ||
public ResponseVO<Map<String, Object>> isValidModel(@RequestParam(name = "modelName") String modelName) { | ||
Map<String, Object> result = chatGPTService.isValidModel(modelName); | ||
return new ResponseVO<>(result); | ||
} | ||
|
||
|
||
/** | ||
* 최신 ChatGPT 프롬프트 명령어를 수행 : | ||
* gpt-4, gpt-4 turbo, gpt-3.5-turbo | ||
*/ | ||
@PostMapping("/prompt") | ||
@Operation(summary = "프롬프트 명령어를 수행", description = "GPT API 구현을 위한 프롬프트 명령어를 수행합니다.") | ||
public ResponseVO<Map<String, Object>> selectPrompt(@RequestBody ChatCompletionDTO chatCompletionDto) { | ||
log.debug("param :: " + chatCompletionDto.toString()); | ||
Map<String, Object> result = chatGPTService.prompt(chatCompletionDto); | ||
return new ResponseVO<>(result); | ||
} | ||
|
||
/** | ||
* 써밋 생성 | ||
*/ | ||
// @PostMapping("/summit") | ||
// @Operation(summary = "써밋 생성", description = "주기적(1주일에 2번) 써밋을 자동으로 생성해줍니다.") | ||
// public ResponseVO<Map<String, Object>> createSummit() { | ||
// log.info("Summit creation request without input payload"); | ||
// | ||
// Map<String, Object> result = chatGPTService.createSummit(); | ||
// return new ResponseVO<>(result); | ||
// } | ||
} |
26 changes: 26 additions & 0 deletions
26
src/main/java/univ/yesummit/domain/gpt/dto/ChatCompletionDTO.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,26 @@ | ||
package univ.yesummit.domain.gpt.dto; | ||
|
||
import lombok.*; | ||
|
||
import java.util.List; | ||
|
||
/** | ||
* 새로운 모델에 대한 요청 객체를 관리한다. | ||
* gpt-4, gpt-4 turbo, gpt-3.5-turbo | ||
*/ | ||
@Getter | ||
@ToString | ||
@NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
public class ChatCompletionDTO { | ||
|
||
// 사용할 모델 | ||
private String model; | ||
|
||
private List<ChatRequestMsgDTO> messages; | ||
|
||
@Builder | ||
public ChatCompletionDTO(String model, List<ChatRequestMsgDTO> messages) { | ||
this.model = model; | ||
this.messages = messages; | ||
} | ||
} |
22 changes: 22 additions & 0 deletions
22
src/main/java/univ/yesummit/domain/gpt/dto/ChatRequestMsgDTO.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,22 @@ | ||
package univ.yesummit.domain.gpt.dto; | ||
|
||
import lombok.*; | ||
|
||
/** | ||
* ChatGPT 신규 모델의 Request | ||
*/ | ||
@Getter | ||
@ToString | ||
@NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
public class ChatRequestMsgDTO { | ||
|
||
private String role; | ||
|
||
private String content; | ||
|
||
@Builder | ||
public ChatRequestMsgDTO(String role, String content) { | ||
this.role = role; | ||
this.content = content; | ||
} | ||
} |
33 changes: 33 additions & 0 deletions
33
src/main/java/univ/yesummit/domain/gpt/dto/CompletionDTO.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,33 @@ | ||
package univ.yesummit.domain.gpt.dto; | ||
|
||
import lombok.*; | ||
|
||
/** | ||
* 프롬프트 요청 DTO : | ||
* gpt-3.5-turbo-instruct, babbage-002, davinci-002 | ||
*/ | ||
@Getter | ||
@ToString | ||
@NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
public class CompletionDTO { | ||
|
||
// 사용할 모델 | ||
private String model; | ||
|
||
// 사용할 프롬프트 명령어 | ||
private String prompt; | ||
|
||
// 프롬프트의 다양성을 조절할 명령어(default : 1) | ||
private float temperature = 1; | ||
|
||
// 최대 사용할 토큰(default : 16) | ||
private int max_tokens = 16; | ||
|
||
@Builder | ||
public CompletionDTO(String model, String prompt, float temperature, int max_tokens) { | ||
this.model = model; | ||
this.prompt = prompt; | ||
this.temperature = temperature; | ||
this.max_tokens = max_tokens; | ||
} | ||
} |
21 changes: 21 additions & 0 deletions
21
src/main/java/univ/yesummit/domain/gpt/service/ChatGPTService.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,21 @@ | ||
package univ.yesummit.domain.gpt.service; | ||
|
||
|
||
import univ.yesummit.domain.gpt.dto.ChatCompletionDTO; | ||
|
||
import java.util.Map; | ||
|
||
public interface ChatGPTService { | ||
|
||
|
||
// 유효한 모델 체크 | ||
Map<String, Object> isValidModel(String modelName); | ||
|
||
|
||
// 최신 모델 요청 | ||
Map<String, Object> prompt(ChatCompletionDTO chatCompletionDto); | ||
|
||
// 써밋 생성 | ||
// Map<String, Object> createSummit(); | ||
|
||
} |
169 changes: 169 additions & 0 deletions
169
src/main/java/univ/yesummit/domain/gpt/service/impl/ChatGPTServiceImpl.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,169 @@ | ||
package univ.yesummit.domain.gpt.service.impl; | ||
|
||
|
||
import com.fasterxml.jackson.core.JsonProcessingException; | ||
import com.fasterxml.jackson.core.type.TypeReference; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
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 univ.yesummit.domain.gpt.config.ChatGPTConfig; | ||
import univ.yesummit.domain.gpt.dto.ChatCompletionDTO; | ||
import univ.yesummit.domain.summit.repository.SummitRepository; | ||
import univ.yesummit.domain.gpt.service.ChatGPTService; | ||
import univ.yesummit.domain.member.repository.MemberRepository; | ||
|
||
import java.util.HashMap; | ||
import java.util.Map; | ||
|
||
/** | ||
* ChatGPT Service 구현체 | ||
*/ | ||
@Slf4j | ||
@Service | ||
@RequiredArgsConstructor | ||
public class ChatGPTServiceImpl implements ChatGPTService { | ||
|
||
private final ChatGPTConfig chatGPTConfig; | ||
private final ObjectMapper objectMapper; | ||
private final SummitRepository summitRepository; | ||
private final MemberRepository memberRepository; | ||
|
||
@Value("${openai.url.model}") | ||
private String modelUrl; | ||
|
||
@Value("${openai.url.model-list}") | ||
private String modelListUrl; | ||
|
||
@Value("${openai.url.prompt}") | ||
private String promptUrl; | ||
|
||
@Value("${openai.url.legacy-prompt}") | ||
private String legacyPromptUrl; | ||
|
||
|
||
/** | ||
* 모델이 유효한지 확인하는 비즈니스 로직 | ||
*/ | ||
@Override | ||
public Map<String, Object> isValidModel(String modelName) { | ||
log.debug("[+] 모델이 유효한지 조회합니다. 모델 : " + modelName); | ||
Map<String, Object> result = new HashMap<>(); | ||
|
||
// 토큰 정보가 포함된 Header를 가져온다. | ||
HttpHeaders headers = chatGPTConfig.httpHeaders(); | ||
|
||
// 통신을 위한 RestTemplate을 구성 | ||
ResponseEntity<String> response = chatGPTConfig | ||
.restTemplate() | ||
.exchange(modelListUrl + "/" + modelName, HttpMethod.GET, new HttpEntity<>(headers), String.class); | ||
try { | ||
// Jackson을 기반으로 응답값을 가져온다. | ||
result = objectMapper.readValue(response.getBody(), new TypeReference<>() { | ||
}); | ||
} catch (JsonProcessingException e) { | ||
log.debug("JsonMappingException :: " + e.getMessage()); | ||
} catch (RuntimeException e) { | ||
log.debug("RuntimeException :: " + e.getMessage()); | ||
} | ||
return result; | ||
} | ||
|
||
|
||
/** | ||
* 최신 모델에 대한 프롬프트 | ||
*/ | ||
@Override | ||
public Map<String, Object> prompt(ChatCompletionDTO chatCompletionDto) { | ||
log.debug("[+] 신규 프롬프트를 수행합니다."); | ||
|
||
Map<String, Object> resultMap = new HashMap<>(); | ||
|
||
// 토큰 정보가 포함된 Header를 가져온다. | ||
HttpHeaders headers = chatGPTConfig.httpHeaders(); | ||
|
||
// 통신을 위한 RestTemplate을 구성 | ||
HttpEntity<ChatCompletionDTO> requestEntity = new HttpEntity<>(chatCompletionDto, headers); | ||
ResponseEntity<String> response = chatGPTConfig | ||
.restTemplate() | ||
.exchange(promptUrl, HttpMethod.POST, requestEntity, String.class); | ||
try { | ||
// String -> HashMap 역직렬화를 구성 | ||
resultMap = objectMapper.readValue(response.getBody(), new TypeReference<>() { | ||
}); | ||
} catch (JsonProcessingException e) { | ||
log.debug("JsonMappingException :: " + e.getMessage()); | ||
} catch (RuntimeException e) { | ||
log.debug("RuntimeException :: " + e.getMessage()); | ||
} | ||
return resultMap; | ||
} | ||
|
||
// @Override | ||
// public Map<String, Object> createSummit() { | ||
// String prompt = "지속 가능한 미래를 이끄는 건, 결국 청년과 그들의 새로운 시각입니다. " + | ||
// "이 점에서 청년들이 그들의 반짝이는 아이디어를 실현하고 세상에 선보일 수 있도록 돕는 것은 " + | ||
// "청년의 창업 문제 해결을 통해 지속 가능한 미래를 만들 수 있습니다. 다만 예비 청년 창업가들에게는 " + | ||
// "자신의 창업 아이템을 전문적으로 소개할 창구가 부족합니다. 그래서 Young Entrepreneur들이 " + | ||
// "주인공인 Summit 플랫폼을 만들고자 합니다. 이를 바탕으로 써밋 주제를 5줄 정도로 만들어주세요. " + | ||
// "제목과 본문으로 구성해 주세요."; | ||
// | ||
// ChatCompletionDTO chatCompletionDto = ChatCompletionDTO.builder() | ||
// .model("gpt-3.5-turbo") | ||
// .messages(List.of( | ||
// ChatRequestMsgDTO.builder() | ||
// .role("user") | ||
// .content(prompt) | ||
// .build() | ||
// )) | ||
// .build(); | ||
// | ||
// // ChatGPT API 호출 | ||
// Map<String, Object> gptResponse = prompt(chatCompletionDto); | ||
// | ||
// // 응답 데이터 검증 | ||
// if (gptResponse == null || !gptResponse.containsKey("choices")) { | ||
// throw new RuntimeException("ChatGPT 응답이 유효하지 않습니다."); | ||
// } | ||
// | ||
// List<Map<String, Object>> choices = (List<Map<String, Object>>) gptResponse.get("choices"); | ||
// if (choices == null || choices.isEmpty()) { | ||
// throw new RuntimeException("ChatGPT 응답에서 choices가 비어 있습니다."); | ||
// } | ||
// | ||
// Map<String, Object> firstChoice = choices.get(0); | ||
// if (firstChoice == null || !firstChoice.containsKey("text")) { | ||
// throw new RuntimeException("ChatGPT 응답의 텍스트가 없습니다."); | ||
// } | ||
// | ||
// String responseText = (String) firstChoice.get("text"); | ||
// if (responseText == null || responseText.isEmpty()) { | ||
// throw new RuntimeException("ChatGPT 응답의 텍스트가 비어 있습니다."); | ||
// } | ||
// | ||
// // 텍스트 파싱 | ||
// String[] splitText = responseText.split("\n", 2); | ||
// if (splitText.length < 2) { | ||
// throw new RuntimeException("ChatGPT 응답 형식이 올바르지 않습니다."); | ||
// } | ||
// | ||
// String title = splitText[0].replace("제목:", "").trim(); | ||
// String content = splitText[1].replace("본문:", "").trim(); | ||
// | ||
// // Summit 엔티티 생성 및 저장 | ||
// Summit newSummit = new Summit(title, content); | ||
// Summit savedSummit = summitRepository.save(newSummit); | ||
// | ||
// return Map.of( | ||
// "message", "Summit has been created successfully", | ||
// "summitId", savedSummit.getId(), | ||
// "title", savedSummit.getTitle(), | ||
// "content", savedSummit.getContent() | ||
// ); | ||
// } | ||
} |
Oops, something went wrong.