Skip to content

Commit

Permalink
feat: Chat GPT Service
Browse files Browse the repository at this point in the history
  • Loading branch information
DDonghyeo committed Jan 11, 2024
1 parent 2806dcd commit 98a4dc0
Show file tree
Hide file tree
Showing 17 changed files with 576 additions and 19 deletions.
4 changes: 3 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-websocket'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'com.mysql:mysql-connector-j'
Expand All @@ -43,7 +44,8 @@ dependencies {
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-api:2.0.4'

// ChatGPT
implementation 'io.github.flashvayne:chatgpt-spring-boot-starter:1.0.4'
// implementation 'io.github.flashvayne:chatgpt-spring-boot-starter:1.0.4'
implementation 'com.theokanning.openai-gpt3-java:service:0.12.0'
}

tasks.named('test') {
Expand Down
25 changes: 25 additions & 0 deletions src/main/java/com/kim3ho1/yourprotein/gpt/common/BaseEntity.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.kim3ho1.yourprotein.gpt.common;

import jakarta.persistence.Column;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.MappedSuperclass;
import lombok.Getter;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import java.time.LocalDate;

@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
@Getter
public class BaseEntity {

@CreatedDate
@Column(updatable = false)
private LocalDate createdDate;

@LastModifiedDate
private LocalDate lastModifiedDate;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.kim3ho1.yourprotein.gpt.config;

import com.theokanning.openai.service.OpenAiService;
import java.time.Duration;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/*
* Chat GPT 라이브러리를 사용하기전, 해당 서비스에 토큰 주입을 하기위한 Config
* API Key 발급은 https://platform.openai.com/account/api-keys 에서 발급받을 수 있습니다.
*/
@Slf4j
@Configuration
public class ChatGPTConfig {

@Value("${gpt.token}")
private String token;

@Bean
public OpenAiService openAiService() {
log.info("token : {}을 활용한 OpenAiService 을 생성합니다.", token);
return new OpenAiService(token, Duration.ofSeconds(60));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//package com.kim3ho1.yourprotein.gpt.config;
//
//import com.querydsl.jpa.impl.JPAQueryFactory;
//import org.springframework.context.annotation.Bean;
//import org.springframework.context.annotation.Configuration;
//
//import javax.persistence.EntityManager;
//import javax.persistence.PersistenceContext;
//
//@Configuration
//public class QueryDslConfig {
//
// @PersistenceContext
// private EntityManager entityManager;
//
// @Bean
// public JPAQueryFactory jpaQueryFactory() {
// return new JPAQueryFactory(entityManager);
// }
//}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.kim3ho1.yourprotein.gpt.config;


import com.kim3ho1.yourprotein.gpt.service.StreamCompletionHandler;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;

@Configuration
@EnableWebSocket
@RequiredArgsConstructor
public class WebsocketConfig implements WebSocketConfigurer {

private final StreamCompletionHandler streamCompletionHandler;

@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(streamCompletionHandler, "/chat/stream").setAllowedOrigins("*");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.kim3ho1.yourprotein.gpt.controller;


import com.kim3ho1.yourprotein.gpt.dto.request.GPTCompletionChatRequest;
import com.kim3ho1.yourprotein.gpt.dto.request.GPTCompletionRequest;
import com.kim3ho1.yourprotein.gpt.dto.response.CompletionChatResponse;
import com.kim3ho1.yourprotein.gpt.dto.response.CompletionResponse;
import com.kim3ho1.yourprotein.gpt.service.GPTChatRestService;
import lombok.RequiredArgsConstructor;
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;

import java.util.HashMap;

@RestController
@RequestMapping("/api/chatgpt/rest")
@RequiredArgsConstructor
public class ChatGPTRestController {

private final GPTChatRestService gptChatRestService;

@PostMapping("/completion/chat")
public CompletionChatResponse chat(final @RequestBody HashMap<String, String> prompt) {
return completionChat(new GPTCompletionChatRequest("gpt-3.5-turbo", "user", prompt.get("prompt"), 1000));
}


public CompletionChatResponse completionChat(final GPTCompletionChatRequest gptCompletionChatRequest) {

return gptChatRestService.completionChat(gptCompletionChatRequest);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.kim3ho1.yourprotein.gpt.dto.request;

import com.theokanning.openai.completion.chat.ChatCompletionRequest;
import com.theokanning.openai.completion.chat.ChatMessage;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
@AllArgsConstructor
public class GPTCompletionChatRequest {

private String model;

private String role;

private String message;

private Integer maxTokens;


public static ChatCompletionRequest of(GPTCompletionChatRequest request) {
return ChatCompletionRequest.builder()
.model(request.getModel())
.messages(convertChatMessage(request))
.maxTokens(request.getMaxTokens())
.build();
}

private static List<ChatMessage> convertChatMessage(GPTCompletionChatRequest request) {
return List.of(new ChatMessage(request.getRole(), request.getMessage()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.kim3ho1.yourprotein.gpt.dto.request;

import com.theokanning.openai.completion.CompletionRequest;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
@AllArgsConstructor
public class GPTCompletionRequest {

private String model;

private String prompt;

private Integer maxToken;


public static CompletionRequest of(GPTCompletionRequest restRequest) {
return CompletionRequest.builder()
.model(restRequest.getModel())
.prompt(restRequest.getPrompt())
.maxTokens(restRequest.getMaxToken())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package com.kim3ho1.yourprotein.gpt.dto.response;


import com.theokanning.openai.completion.chat.ChatCompletionChoice;
import com.theokanning.openai.completion.chat.ChatCompletionResult;
import com.theokanning.openai.completion.chat.ChatMessage;
import java.util.List;
import java.util.stream.Collectors;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
@AllArgsConstructor
public class CompletionChatResponse {

private String id;

private String object;

private Long created;

private String model;

private List<Message> messages;

private Usage usage;

@Getter
@NoArgsConstructor
@AllArgsConstructor
public static class Message {

private String role;

private String message;

public static Message of(ChatMessage chatMessage) {
return new Message(
chatMessage.getRole(),
chatMessage.getContent()
);
}
}


@Getter
@NoArgsConstructor
@AllArgsConstructor
public static class Usage {

private Long promptTokens;

private Long completionTokens;

private Long totalTokens;

public static Usage of(com.theokanning.openai.Usage usage) {
return new Usage(
usage.getPromptTokens(),
usage.getCompletionTokens(),
usage.getTotalTokens()
);
}
}

public static List<CompletionChatResponse.Message> toResponseListBy(List<ChatCompletionChoice> choices) {
return choices.stream()
.map(completionChoice -> Message.of(completionChoice.getMessage()))
.collect(Collectors.toList());
}

public static CompletionChatResponse of(ChatCompletionResult result) {
return new CompletionChatResponse(
result.getId(),
result.getObject(),
result.getCreated(),
result.getModel(),
toResponseListBy(result.getChoices()),
Usage.of(result.getUsage())
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package com.kim3ho1.yourprotein.gpt.dto.response;

import com.theokanning.openai.completion.CompletionChoice;
import com.theokanning.openai.completion.CompletionResult;
import java.util.List;
import java.util.stream.Collectors;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
@AllArgsConstructor
public class CompletionResponse {

private String id;

private String object;

private Long created;

private String model;

private List<Message> messages;

private Usage usage;


@Getter
@NoArgsConstructor
@AllArgsConstructor
public static class Message {

private String text;

private Integer index;

private String finishReason;

public static Message of(CompletionChoice choice) {
return new Message(
choice.getText(),
choice.getIndex(),
choice.getFinish_reason()
);
}
}

@Getter
@NoArgsConstructor
@AllArgsConstructor
public static class Usage {

private Long promptTokens;

private Long completionTokens;

private Long totalTokens;

public static Usage of(com.theokanning.openai.Usage usage) {
return new Usage(
usage.getPromptTokens(),
usage.getCompletionTokens(),
usage.getTotalTokens()
);
}
}

public static List<Message> toResponseListBy(List<CompletionChoice> choices) {
return choices.stream()
.map(Message::of)
.collect(Collectors.toList());
}

public static CompletionResponse of(CompletionResult result) {
return new CompletionResponse(
result.getId(),
result.getObject(),
result.getCreated(),
result.getModel(),
toResponseListBy(result.getChoices()),
Usage.of(result.getUsage())
);
}
}
Loading

0 comments on commit 98a4dc0

Please sign in to comment.