Skip to content

Commit

Permalink
Merge pull request #39 from Art-Chain/develop
Browse files Browse the repository at this point in the history
릴리즈 1.0.0
  • Loading branch information
woosung1223 authored Jul 26, 2024
2 parents 48ab363 + 28c92cf commit 8ec8cdd
Show file tree
Hide file tree
Showing 59 changed files with 1,690 additions and 21 deletions.
10 changes: 10 additions & 0 deletions .cloudtype/farmpro-backend.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
name: farmpro-backend
app: java@17
options:
ports: 8080
env: []
buildenv: []
context:
git:
url: [email protected]:Art-Chain/FarmPro-Backend.git
branch: develop
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
HELP.md
.gradle
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/

Expand Down
106 changes: 105 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,105 @@
# FarmPro-Backend
# FarmPro-Backend

## 프로젝트 소개

> FarmPro는 생성형 AI를 사용하여 농업 생산자에게 최적의 마케팅 전략을 제공합니다.
> 이 레포지토리는 FarmPro의 Backend infra, ML Service를 담당합니다.
![FarmPro-logo.png](docs/FarmPro-logo.png)

## API 문서

[API Documentation](https://port-0-farmpro-backend-lypd5head859a5e8.sel5.cloudtype.app/swagger-ui/index.html)

## 기술 스택

- `Java` 17
- `Spring Boot` 3.3.1
- `Spring Data JPA`
- `Spring WebFlux`
- `Spring Cloud` 2020.0.3
- `Spring Cloud AWS` 2.4.0

## 프로젝트 구조

```Bash

├── LICENSE
├── README.md
├── build
├── build.gradle
├── docs
├── gradle
├── gradlew
├── gradlew.bat
├── settings.gradle
└── src
├── java
│   └── artchain
│   └── farmpro
│   ├── FarmproApplication.java
│   ├── ai
│   │   ├── ChatGptConfiguration.java
│   │   ├── ChatGptWebClient.java
│   │   ├── dto
│   │   │   ├── ChatGptResponse.java
│   │   │   └── GPTRecommendResponse.java
│   │   └── prompt
│   │   ├── PromptContext.java
│   │   ├── PromptPropertyParser.java
│   │   ├── PromptStrategy.java
│   │   ├── SnsContentPromptStrategy.java
│   │   └── SnsImagePromptStrategy.java
│   ├── card
│   │   ├── Card.java
│   │   ├── CardRepository.java
│   │   ├── CardStyle.java
│   │   └── dto
│   │   ├── CardRequest.java
│   │   └── CardsRequest.java
│   ├── content
│   │   ├── Content.java
│   │   ├── ContentController.java
│   │   ├── ContentPurpose.java
│   │   ├── ContentRepository.java
│   │   ├── ContentService.java
│   │   ├── ContentType.java
│   │   ├── ParlanceStyle.java
│   │   ├── dto
│   │   │   ├── ContentCreateResponse.java
│   │   │   ├── ContentRecommendRequest.java
│   │   │   ├── ContentRecommendResponse.java
│   │   │   ├── ContentRequest.java
│   │   │   ├── ContentResponse.java
│   │   │   ├── ContentResponses.java
│   │   │   ├── ProjectInfoRequest.java
│   │   │   └── ProjectInfoResponse.java
│   │   └── image
│   │   ├── ContentImage.java
│   │   ├── ContentImageController.java
│   │   ├── ContentImageService.java
│   │   └── dto
│   │   ├── ContentImagePresignedUrlVO.java
│   │   ├── ContentImageResponse.java
│   │   ├── ContentImageResponses.java
│   │   └── NotifyContentImageSaveSuccessRequest.java
│   ├── crop
│   │   ├── Crop.java
│   │   ├── CropController.java
│   │   ├── CropRepository.java
│   │   ├── CropService.java
│   │   └── dto
│   │   ├── CropRequest.java
│   │   ├── CropResponse.java
│   │   └── CropResponses.java
│   ├── global
│   │   ├── config
│   │   │   ├── S3Config.java
│   │   │   ├── SwaggerConfig.java
│   │   │   └── WebConfig.java
│   │   └── health
│   │   └── HealthController.java
│   └── selectedcrop
│   ├── SelectedCrop.java
│   └── SelectedCropRepository.java

41 changes: 23 additions & 18 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,38 +1,43 @@
plugins {
id 'java'
id 'org.springframework.boot' version '3.3.1'
id 'io.spring.dependency-management' version '1.1.5'
id 'java'
id 'org.springframework.boot' version '3.3.1'
id 'io.spring.dependency-management' version '1.1.5'
}

group = 'artchain'
version = '0.0.1-SNAPSHOT'

java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}

configurations {
compileOnly {
extendsFrom annotationProcessor
}
compileOnly {
extendsFrom annotationProcessor
}
}

repositories {
mavenCentral()
mavenCentral()
}

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.h2database:h2'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-webflux'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.h2database:h2'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.4'
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-api:2.0.4'
implementation 'io.awspring.cloud:spring-cloud-starter-aws:2.4.4'
implementation 'io.awspring.cloud:spring-cloud-starter-aws-secrets-manager-config:2.4.4'
}

tasks.named('test') {
useJUnitPlatform()
useJUnitPlatform()
}
1 change: 1 addition & 0 deletions docs/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* @loveysuby @woosung1223
Binary file added docs/FarmPro-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
25 changes: 25 additions & 0 deletions src/main/java/artchain/farmpro/ai/ChatGptConfiguration.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package artchain.farmpro.ai;

import lombok.Getter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Getter
@Component
public class ChatGptConfiguration {

@Value("${openai.key}")
private String openaiKey;
@Value("${openai.image-generation.endpoint}")
private String imageModelEndpoint;
@Value("${openai.text-generation.endpoint}")
private String textModelEndpoint;
@Value("${openai.image-generation.model}")
private String imageModel;
@Value("${openai.text-generation.model}")
private String textModel;
@Value("${openai.image-generation.n}")
private String n;
@Value("${openai.image-generation.size}")
private String size;
}
84 changes: 84 additions & 0 deletions src/main/java/artchain/farmpro/ai/ChatGptWebClient.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package artchain.farmpro.ai;

import artchain.farmpro.ai.dto.ChatGptResponse;
import artchain.farmpro.ai.dto.GPTRecommendResponse;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import lombok.AllArgsConstructor;
import org.springframework.http.HttpStatusCode;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

@AllArgsConstructor
@Component
public class ChatGptWebClient {

private ChatGptConfiguration configuration;

public Mono<ChatGptResponse> requestImageGenerate(String prompt) {
HashMap<String, Object> body = setupImageBodyOutput(prompt);

return WebClient.create()
.post()
.uri(configuration.getImageModelEndpoint())
.header("Content-Type", "application/json")
.header("Authorization", "Bearer " + configuration.getOpenaiKey())
.bodyValue(body)
.retrieve()
.onStatus(HttpStatusCode::is4xxClientError,
clientResponse -> clientResponse.bodyToMono(String.class).map(Exception::new))
.bodyToMono(ChatGptResponse.class);
}

public GPTRecommendResponse requestTextGenerate(String prompt) {
HashMap<String, Object> body = setupTextBodyOutput(prompt);

return WebClient.create()
.post()
.uri(configuration.getTextModelEndpoint())
.header("Content-Type", "application/json")
.header("Authorization", "Bearer " + configuration.getOpenaiKey())
.bodyValue(body)
.retrieve()
.bodyToMono(GPTRecommendResponse.class)
.block();
}

public GPTRecommendResponse requestKeywordGenerate(String prompt) {
HashMap<String, Object> body = setupTextBodyOutput(prompt);

return WebClient.create()
.post()
.uri(configuration.getTextModelEndpoint())
.header("Content-Type", "application/json")
.header("Authorization", "Bearer " + configuration.getOpenaiKey())
.bodyValue(body)
.retrieve()
.bodyToMono(GPTRecommendResponse.class)
.block();
}

private HashMap<String, Object> setupImageBodyOutput(String prompt) {
HashMap<String, Object> body = new HashMap<>();
body.put("model", configuration.getImageModel());
body.put("n", Integer.parseInt(configuration.getN()));
body.put("size", configuration.getSize());
body.put("prompt", prompt);
return body;
}

private HashMap<String, Object> setupTextBodyOutput(String prompt) {
HashMap<String, Object> body = new HashMap<>();
body.put("model", configuration.getTextModel());
List<HashMap<String, String>> messages = new ArrayList<>();
HashMap<String, String> userMessage = new HashMap<>();
userMessage.put("role", "user");
userMessage.put("content", prompt);
messages.add(userMessage);
body.put("messages", messages);

return body;
}
}
16 changes: 16 additions & 0 deletions src/main/java/artchain/farmpro/ai/dto/ChatGptResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package artchain.farmpro.ai.dto;

import java.util.List;

public record ChatGptResponse(List<UrlResponse> data) {

public record UrlResponse(String url, String revisedPrompt) {
}

public static List<String> urlsFrom(List<ChatGptResponse> responses) {
return responses.stream()
.flatMap(response -> response.data().stream())
.map(UrlResponse::url)
.toList();
}
}
35 changes: 35 additions & 0 deletions src/main/java/artchain/farmpro/ai/dto/GPTRecommendResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package artchain.farmpro.ai.dto;

import java.util.List;

public record GPTRecommendResponse(
String id,
String object,
long created,
String model,
String system_fingerprint,
List<Choice> choices,
Usage usage
) {
public String getContent() {
return choices.get(0).message().content();
}

public record Choice(
int index,
Message message,
Object logprobs,
String finish_reason
) {}

public record Message(
String role,
String content
) {}

public record Usage(
int prompt_tokens,
int completion_tokens,
int total_tokens
) {}
}
27 changes: 27 additions & 0 deletions src/main/java/artchain/farmpro/ai/prompt/PromptContext.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package artchain.farmpro.ai.prompt;

import jakarta.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.apache.coyote.BadRequestException;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
public class PromptContext {
private final List<PromptStrategy> promptStrategies = new ArrayList<>();

@PostConstruct
void initSocialLoginContext() {
promptStrategies.add(new SnsImagePromptStrategy());
promptStrategies.add(new SnsContentPromptStrategy());
}

public String executePrompt(final PromptPropertyParser request) throws BadRequestException {
for (PromptStrategy strategy : promptStrategies) {
return strategy.createParsedPrompt(request);
}
throw new BadRequestException("잘못된 형식의 모델 요청입니다.");
}
}
10 changes: 10 additions & 0 deletions src/main/java/artchain/farmpro/ai/prompt/PromptPropertyParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package artchain.farmpro.ai.prompt;

import artchain.farmpro.card.CardStyle;
import artchain.farmpro.content.ContentPurpose;
import artchain.farmpro.content.ContentType;
import artchain.farmpro.content.ParlanceStyle;

public record PromptPropertyParser(String objectName, ContentType contentType, ContentPurpose contentPurpose,
String mainText, ParlanceStyle parlanceStyle, CardStyle cardStyle) {
}
Loading

0 comments on commit 8ec8cdd

Please sign in to comment.