Skip to content

Commit

Permalink
Merge pull request #37 from 9oormthon-univ/dev
Browse files Browse the repository at this point in the history
GPT for Summit
  • Loading branch information
sumin220 authored Nov 22, 2024
2 parents 6934218 + 7c9c20f commit 5e1fa4f
Show file tree
Hide file tree
Showing 14 changed files with 505 additions and 0 deletions.
30 changes: 30 additions & 0 deletions src/main/java/univ/yesummit/domain/gpt/config/ChatGPTConfig.java
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;
}
}
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 src/main/java/univ/yesummit/domain/gpt/dto/ChatCompletionDTO.java
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 src/main/java/univ/yesummit/domain/gpt/dto/ChatRequestMsgDTO.java
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 src/main/java/univ/yesummit/domain/gpt/dto/CompletionDTO.java
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 src/main/java/univ/yesummit/domain/gpt/service/ChatGPTService.java
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();

}
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()
// );
// }
}
Loading

0 comments on commit 5e1fa4f

Please sign in to comment.