openai연동 및 ai 서비스 생성
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
package com.caliverse.admin.domain.api;
|
||||
|
||||
import com.caliverse.admin.domain.request.AIRequest;
|
||||
import com.caliverse.admin.domain.response.AIResponse;
|
||||
import com.caliverse.admin.domain.service.AIService;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
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;
|
||||
|
||||
@Tag(name = "AI 관련", description = "AI api 입니다.")
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/api/v1/ai")
|
||||
|
||||
public class AIController {
|
||||
|
||||
private final AIService aiService;
|
||||
|
||||
@PostMapping("/analyze")
|
||||
public ResponseEntity<AIResponse> aiAnalyze(@RequestBody AIRequest dataRequest){
|
||||
return ResponseEntity.ok().body(aiService.aiMessageAnalyze(dataRequest));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.caliverse.admin.domain.entity.AI;
|
||||
|
||||
public enum AIRequestType {
|
||||
BUSINESS_LOG,
|
||||
MAIL
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.caliverse.admin.domain.entity.AI;
|
||||
|
||||
public enum AIRole {
|
||||
user,
|
||||
system,
|
||||
assistant
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.caliverse.admin.domain.request;
|
||||
|
||||
import com.caliverse.admin.domain.entity.AI.AIRequestType;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class AIRequest {
|
||||
private String message;
|
||||
private AIRequestType type;
|
||||
private Map<String, Object> conditions;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.caliverse.admin.domain.request;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class OpenAIRequest {
|
||||
private String model;
|
||||
private List<Map<String, String>> messages;
|
||||
@Builder.Default
|
||||
private double temperature = 0.7;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.caliverse.admin.domain.response;
|
||||
|
||||
import com.caliverse.admin.history.domain.DynamodbDataInitializeHistory;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class AIResponse {
|
||||
private int status;
|
||||
|
||||
private String result;
|
||||
|
||||
@JsonProperty("data")
|
||||
private ResultData resultData;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public static class ResultData {
|
||||
|
||||
private String message;
|
||||
private String result;
|
||||
|
||||
private int total;
|
||||
@JsonProperty("total_all")
|
||||
private int totalAll;
|
||||
@JsonProperty("page_no")
|
||||
private int pageNo;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.caliverse.admin.domain.response;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class OpenAIResponse {
|
||||
private List<Choice> choices;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public static class Choice {
|
||||
private Message message;
|
||||
}
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public static class Message {
|
||||
private String role;
|
||||
private String content;
|
||||
}
|
||||
}
|
||||
110
src/main/java/com/caliverse/admin/domain/service/AIService.java
Normal file
110
src/main/java/com/caliverse/admin/domain/service/AIService.java
Normal file
@@ -0,0 +1,110 @@
|
||||
package com.caliverse.admin.domain.service;
|
||||
|
||||
import com.caliverse.admin.domain.dao.admin.MailMapper;
|
||||
import com.caliverse.admin.domain.entity.AI.AIRequestType;
|
||||
import com.caliverse.admin.domain.entity.AI.AIRole;
|
||||
import com.caliverse.admin.domain.entity.Mail;
|
||||
import com.caliverse.admin.domain.request.AIRequest;
|
||||
import com.caliverse.admin.domain.request.LogGenericRequest;
|
||||
import com.caliverse.admin.domain.request.OpenAIRequest;
|
||||
import com.caliverse.admin.domain.response.AIResponse;
|
||||
import com.caliverse.admin.domain.response.OpenAIResponse;
|
||||
import com.caliverse.admin.global.common.code.CommonCode;
|
||||
import com.caliverse.admin.global.common.constants.CommonConstants;
|
||||
import com.caliverse.admin.global.common.utils.CommonUtils;
|
||||
import com.caliverse.admin.logs.Indicatordomain.GenericMongoLog;
|
||||
import com.caliverse.admin.logs.logservice.businesslogservice.BusinessLogGenericService;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class AIService {
|
||||
@Autowired
|
||||
private ObjectMapper mapper;
|
||||
|
||||
private final OpenAIService openAIService;
|
||||
private final BusinessLogGenericService businessLogGenericService;
|
||||
private final MailMapper mailMapper;
|
||||
|
||||
public AIResponse aiMessageAnalyze(AIRequest dataRequest){
|
||||
List<Map<String,String>> messages = initMessage();
|
||||
|
||||
List<?> data = getDataList(dataRequest.getType(), dataRequest.getConditions());
|
||||
|
||||
messages = splitDataToMessages(messages, data, dataRequest.getMessage());
|
||||
|
||||
OpenAIRequest openAIRequest = new OpenAIRequest();
|
||||
openAIRequest.setMessages(messages);
|
||||
|
||||
OpenAIResponse result = openAIService.askOpenAI(openAIRequest);
|
||||
log.info(result.getChoices().get(0).getMessage().getContent());
|
||||
return AIResponse.builder()
|
||||
.resultData(AIResponse.ResultData.builder()
|
||||
.result(result.getChoices().get(0).getMessage().getContent())
|
||||
.build())
|
||||
.status(CommonCode.SUCCESS.getHttpStatus())
|
||||
.result(CommonCode.SUCCESS.getResult())
|
||||
.build();
|
||||
}
|
||||
|
||||
private List<Map<String, String>> splitDataToMessages(List<Map<String,String>> messages, List<?> dataList, String userMessage) {
|
||||
String dataJson;
|
||||
try {
|
||||
dataJson = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(dataList);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException("JSON 변환 실패", e);
|
||||
}
|
||||
messages.add(CommonUtils.getAIMessage(AIRole.user, messageMerge(userMessage, dataJson)));
|
||||
|
||||
return messages;
|
||||
}
|
||||
|
||||
private String messageMerge(String message, String data){
|
||||
return String.format("사용자 질문: %s\n\n아래는 시스템이 제공하는 데이터입니다.\n%s", message, data);
|
||||
}
|
||||
|
||||
private List<Map<String,String>> initMessage(){
|
||||
List<Map<String,String>> messages = new ArrayList<>();
|
||||
messages.add(CommonUtils.getAIMessage(AIRole.system,
|
||||
"""
|
||||
너는 프론트엔드 게시용 데이터를 생성하는 게임 데이터 분석 AI야. \
|
||||
사용자는 게임 로그 데이터와 함께 다양한 질문을 보낼 수 있어. \
|
||||
너는 항상 HTML 또는 React 코드 형식으로 응답해야 해. \
|
||||
응답에는 반드시 다음 항목을 포함해줘: \
|
||||
1) 분석된 내용 설명 \
|
||||
2) 분석된 요약 결과 (표 또는 문단) \
|
||||
3) 시각화 차트 (예: bar chart, pie chart 등) \
|
||||
응답은 반드시 한국어로 작성해. \
|
||||
절대로 파이썬, Java, Node.js 코드 같은 것은 포함하지 마. \
|
||||
사용자의 웹에 그대로 게시될 수 있는 코드만 생성해. \
|
||||
React를 쓸 경우, React 18 기준 코드로 TailwindCSS 기반의 스타일을 사용해줘.
|
||||
"""));
|
||||
|
||||
return messages;
|
||||
}
|
||||
|
||||
private List<?> getDataList(AIRequestType type, Map<String, Object> conditions){
|
||||
switch (type){
|
||||
case BUSINESS_LOG -> {
|
||||
LogGenericRequest logReq = mapper.convertValue(conditions, LogGenericRequest.class);
|
||||
List<GenericMongoLog> logs = businessLogGenericService.loadBusinessLogData(logReq, GenericMongoLog.class, true);
|
||||
return logs;
|
||||
}
|
||||
case MAIL -> {
|
||||
List<Mail> mailList = mailMapper.getMailList(conditions);
|
||||
return mailList;
|
||||
}
|
||||
default -> throw new RuntimeException("Not Type");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,358 @@
|
||||
package com.caliverse.admin.domain.service;
|
||||
|
||||
import com.caliverse.admin.domain.request.OpenAIRequest;
|
||||
import com.caliverse.admin.domain.response.OpenAIResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.http.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
@Slf4j
|
||||
public class OpenAIService {
|
||||
private final RestTemplate restTemplate;
|
||||
|
||||
@Value("${open-ai.url}")
|
||||
private String api_url;
|
||||
|
||||
@Value("${open-ai.key}")
|
||||
private String api_key;
|
||||
|
||||
@Value("${open-ai.chatModel}")
|
||||
private String api_chat_model;
|
||||
|
||||
@Value("${open-ai.assistantsModel}")
|
||||
private String api_assistants_model;
|
||||
|
||||
public OpenAIResponse askOpenAI(OpenAIRequest request) {
|
||||
request.setModel(api_chat_model);
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
headers.setBearerAuth(api_key);
|
||||
|
||||
HttpEntity<OpenAIRequest> entity = new HttpEntity<>(request, headers);
|
||||
|
||||
ResponseEntity<OpenAIResponse> response = restTemplate.exchange(
|
||||
api_url + "chat/completions",
|
||||
HttpMethod.POST,
|
||||
entity,
|
||||
OpenAIResponse.class
|
||||
);
|
||||
|
||||
return response.getBody();
|
||||
}
|
||||
|
||||
// 파일 업로드 메서드
|
||||
public String uploadFile(String filePath) {
|
||||
String url = "https://api.openai.com/v1/files";
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setBearerAuth(api_key);
|
||||
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
|
||||
|
||||
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
|
||||
body.add("file", new FileSystemResource(new File(filePath)));
|
||||
body.add("purpose", "assistants");
|
||||
|
||||
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
|
||||
|
||||
ResponseEntity<Map> response = restTemplate.exchange(
|
||||
url,
|
||||
HttpMethod.POST,
|
||||
requestEntity,
|
||||
Map.class
|
||||
);
|
||||
|
||||
log.info("File uploaded successfully: {}", response.getBody());
|
||||
return (String) response.getBody().get("id");
|
||||
}
|
||||
|
||||
// Assistant 생성 메서드
|
||||
public String createAssistant(String name, String instructions) {
|
||||
String url = "https://api.openai.com/v1/assistants";
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setBearerAuth(api_key);
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
|
||||
Map<String, Object> body = new HashMap<>();
|
||||
body.put("model", api_assistants_model);
|
||||
body.put("name", name);
|
||||
body.put("instructions", instructions);
|
||||
body.put("tools", List.of(Map.of("type", "retrieval")));
|
||||
|
||||
HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(body, headers);
|
||||
|
||||
ResponseEntity<Map> response = restTemplate.exchange(
|
||||
url,
|
||||
HttpMethod.POST,
|
||||
requestEntity,
|
||||
Map.class
|
||||
);
|
||||
|
||||
log.info("Assistant created successfully: {}", response.getBody());
|
||||
return (String) response.getBody().get("id");
|
||||
}
|
||||
|
||||
// Thread 생성 메서드
|
||||
public String createThread() {
|
||||
String url = "https://api.openai.com/v1/threads";
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setBearerAuth(api_key);
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
|
||||
Map<String, Object> body = new HashMap<>();
|
||||
|
||||
HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(body, headers);
|
||||
|
||||
ResponseEntity<Map> response = restTemplate.exchange(
|
||||
url,
|
||||
HttpMethod.POST,
|
||||
requestEntity,
|
||||
Map.class
|
||||
);
|
||||
|
||||
log.info("Thread created successfully: {}", response.getBody());
|
||||
return (String) response.getBody().get("id");
|
||||
}
|
||||
|
||||
// Thread에 메시지 추가 메서드
|
||||
public String addMessageToThread(String threadId, String content, String fileId) {
|
||||
String url = "https://api.openai.com/v1/threads/" + threadId + "/messages";
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setBearerAuth(api_key);
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
|
||||
Map<String, Object> body = new HashMap<>();
|
||||
body.put("role", "user");
|
||||
body.put("content", content);
|
||||
|
||||
if (fileId != null && !fileId.isEmpty()) {
|
||||
body.put("file_ids", Collections.singletonList(fileId));
|
||||
}
|
||||
|
||||
HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(body, headers);
|
||||
|
||||
ResponseEntity<Map> response = restTemplate.exchange(
|
||||
url,
|
||||
HttpMethod.POST,
|
||||
requestEntity,
|
||||
Map.class
|
||||
);
|
||||
|
||||
log.info("Message added to thread successfully: {}", response.getBody());
|
||||
return (String) response.getBody().get("id");
|
||||
}
|
||||
|
||||
// Run 실행 메서드
|
||||
public String startRun(String threadId, String assistantId) {
|
||||
String url = "https://api.openai.com/v1/threads/" + threadId + "/runs";
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setBearerAuth(api_key);
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
|
||||
Map<String, Object> body = new HashMap<>();
|
||||
body.put("assistant_id", assistantId);
|
||||
|
||||
HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(body, headers);
|
||||
|
||||
ResponseEntity<Map> response = restTemplate.exchange(
|
||||
url,
|
||||
HttpMethod.POST,
|
||||
requestEntity,
|
||||
Map.class
|
||||
);
|
||||
|
||||
log.info("Run started successfully: {}", response.getBody());
|
||||
return (String) response.getBody().get("id");
|
||||
}
|
||||
|
||||
// Run 상태 확인 메서드
|
||||
public Map<String, Object> checkRunStatus(String threadId, String runId) {
|
||||
String url = "https://api.openai.com/v1/threads/" + threadId + "/runs/" + runId;
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setBearerAuth(api_key);
|
||||
|
||||
HttpEntity<Void> requestEntity = new HttpEntity<>(headers);
|
||||
|
||||
ResponseEntity<Map> response = restTemplate.exchange(
|
||||
url,
|
||||
HttpMethod.GET,
|
||||
requestEntity,
|
||||
Map.class
|
||||
);
|
||||
|
||||
log.debug("Run status: {}", response.getBody());
|
||||
return response.getBody();
|
||||
}
|
||||
|
||||
// Thread의 메시지 목록 조회 메서드
|
||||
public List<Map<String, Object>> listMessages(String threadId) {
|
||||
String url = "https://api.openai.com/v1/threads/" + threadId + "/messages";
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setBearerAuth(api_key);
|
||||
|
||||
HttpEntity<Void> requestEntity = new HttpEntity<>(headers);
|
||||
|
||||
ResponseEntity<Map> response = restTemplate.exchange(
|
||||
url,
|
||||
HttpMethod.GET,
|
||||
requestEntity,
|
||||
Map.class
|
||||
);
|
||||
|
||||
log.debug("Thread messages: {}", response.getBody());
|
||||
return (List<Map<String, Object>>) response.getBody().get("data");
|
||||
}
|
||||
|
||||
// 마지막 Assistant 메시지 추출 메서드
|
||||
public String extractLastAssistantMessage(String threadId) {
|
||||
List<Map<String, Object>> messages = listMessages(threadId);
|
||||
|
||||
for (Map<String, Object> message : messages) {
|
||||
if ("assistant".equals(message.get("role"))) {
|
||||
List<Map<String, Object>> content = (List<Map<String, Object>>) message.get("content");
|
||||
if (content != null && !content.isEmpty()) {
|
||||
for (Map<String, Object> contentItem : content) {
|
||||
if ("text".equals(contentItem.get("type"))) {
|
||||
Map<String, Object> text = (Map<String, Object>) contentItem.get("text");
|
||||
return (String) text.get("value");
|
||||
}
|
||||
}
|
||||
}
|
||||
break; // 가장 최신 assistant 메시지만 처리
|
||||
}
|
||||
}
|
||||
|
||||
return "No assistant response found";
|
||||
}
|
||||
|
||||
// 파일 삭제 메서드
|
||||
public boolean deleteFile(String fileId) {
|
||||
String url = "https://api.openai.com/v1/files/" + fileId;
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setBearerAuth(api_key);
|
||||
|
||||
HttpEntity<Void> requestEntity = new HttpEntity<>(headers);
|
||||
|
||||
try {
|
||||
ResponseEntity<Map> response = restTemplate.exchange(
|
||||
url,
|
||||
HttpMethod.DELETE,
|
||||
requestEntity,
|
||||
Map.class
|
||||
);
|
||||
|
||||
log.info("File deleted successfully: {}", response.getBody());
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.error("Error deleting file: {}", e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Assistant 가져오기 또는 생성 메서드
|
||||
public String getOrCreateAssistant() {
|
||||
String url = "https://api.openai.com/v1/assistants";
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setBearerAuth(api_key);
|
||||
|
||||
HttpEntity<Void> requestEntity = new HttpEntity<>(headers);
|
||||
|
||||
ResponseEntity<Map> response = restTemplate.exchange(
|
||||
url,
|
||||
HttpMethod.GET,
|
||||
requestEntity,
|
||||
Map.class
|
||||
);
|
||||
|
||||
List<Map<String, Object>> assistants = (List<Map<String, Object>>) response.getBody().get("data");
|
||||
|
||||
// 데이터 분석 어시스턴트 찾기
|
||||
for (Map<String, Object> assistant : assistants) {
|
||||
if ("Data Analyzer".equals(assistant.get("name"))) {
|
||||
return (String) assistant.get("id");
|
||||
}
|
||||
}
|
||||
|
||||
// 없으면 새로 생성
|
||||
return createAssistant("Data Analyzer",
|
||||
"You are a data analysis assistant. Analyze the provided JSON data and generate visualizations. " +
|
||||
"Your responses should be in HTML format with appropriate visualizations using charts. " +
|
||||
"Focus on identifying patterns, trends, and insights from the data. " +
|
||||
"Use Korean language for all responses.");
|
||||
}
|
||||
|
||||
// 전체 비동기 프로세스 실행 및 결과 대기 메서드
|
||||
public String processDataAnalysisAndWait(String filePath, String question) {
|
||||
try {
|
||||
// 1. 파일 업로드
|
||||
String fileId = uploadFile(filePath);
|
||||
|
||||
// 2. Assistant 얻기 또는 생성
|
||||
String assistantId = getOrCreateAssistant();
|
||||
|
||||
// 3. Thread 생성
|
||||
String threadId = createThread();
|
||||
|
||||
// 4. 메시지 추가
|
||||
addMessageToThread(threadId, question, fileId);
|
||||
|
||||
// 5. Run 실행
|
||||
String runId = startRun(threadId, assistantId);
|
||||
|
||||
// 6. 완료 대기 (최대 60초, 3초마다 체크)
|
||||
String status = "queued";
|
||||
int maxAttempts = 20;
|
||||
int attempts = 0;
|
||||
|
||||
while (!status.equals("completed") && attempts < maxAttempts) {
|
||||
Thread.sleep(3000);
|
||||
Map<String, Object> runStatus = checkRunStatus(threadId, runId);
|
||||
status = (String) runStatus.get("status");
|
||||
|
||||
if (status.equals("failed") || status.equals("cancelled")) {
|
||||
throw new RuntimeException("Run failed with status: " + status);
|
||||
}
|
||||
|
||||
attempts++;
|
||||
}
|
||||
|
||||
if (!status.equals("completed")) {
|
||||
throw new RuntimeException("Run timed out after 60 seconds");
|
||||
}
|
||||
|
||||
// 7. 응답 메시지 추출
|
||||
String result = extractLastAssistantMessage(threadId);
|
||||
|
||||
// 8. 파일 정리 (선택적)
|
||||
deleteFile(fileId);
|
||||
|
||||
return result;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Error in data analysis process: {}", e.getMessage());
|
||||
throw new RuntimeException("Data analysis failed: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,8 @@
|
||||
spring:
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: 5MB
|
||||
max-request-size: 10MB
|
||||
## deploy
|
||||
# profiles:
|
||||
# active: stage
|
||||
@@ -25,4 +29,15 @@ spring:
|
||||
password:
|
||||
expiration-days: 180
|
||||
|
||||
open-ai:
|
||||
key: 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
|
||||
url: https://api.openai.com/v1/
|
||||
chatModel: gpt-4o
|
||||
assistantsModel: gpt-4-1106-preview
|
||||
|
||||
claude:
|
||||
key: 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
|
||||
model: claude-3-7-sonnet-20250219
|
||||
url: https://api.anthropic.com/v1/messages
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user