.
This commit is contained in:
@@ -13,10 +13,21 @@ import java.time.Duration;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import kz.konturai.parser.dto.ChartData;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.web.reactive.function.client.WebClientResponseException;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class OpenAiChartService {
|
public class OpenAiChartService {
|
||||||
|
|
||||||
|
// 1. Инициализируем логгер для этого класса
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(OpenAiChartService.class);
|
||||||
|
|
||||||
private final WebClient webClient;
|
private final WebClient webClient;
|
||||||
|
|
||||||
@Value("${openai.model.name:gpt-4o-mini}")
|
@Value("${openai.model.name:gpt-4o-mini}")
|
||||||
@@ -28,6 +39,11 @@ public class OpenAiChartService {
|
|||||||
public OpenAiChartService(
|
public OpenAiChartService(
|
||||||
@Value("${openai.api.url:https://api.openai.com/v1/chat/completions}") String apiUrl,
|
@Value("${openai.api.url:https://api.openai.com/v1/chat/completions}") String apiUrl,
|
||||||
@Value("${openai.api.key:}") String apiKey) {
|
@Value("${openai.api.key:}") String apiKey) {
|
||||||
|
|
||||||
|
if (apiKey == null || apiKey.isBlank()) {
|
||||||
|
logger.warn("OpenAI API key is not configured. OpenAiChartService will not work.");
|
||||||
|
}
|
||||||
|
|
||||||
HttpClient httpClient = HttpClient.create();
|
HttpClient httpClient = HttpClient.create();
|
||||||
this.webClient = WebClient.builder()
|
this.webClient = WebClient.builder()
|
||||||
.baseUrl(apiUrl)
|
.baseUrl(apiUrl)
|
||||||
@@ -37,22 +53,30 @@ public class OpenAiChartService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Mono<String> getChartDataJson(String aggregatedLearnings) {
|
public Mono<String> getChartDataJson(String aggregatedLearnings) {
|
||||||
|
// 2. Логируем начало важной операции
|
||||||
|
logger.info("Requesting JSON data for charts from OpenAI...");
|
||||||
String prompt = buildChartDataPrompt(aggregatedLearnings);
|
String prompt = buildChartDataPrompt(aggregatedLearnings);
|
||||||
return callChatCompletions(prompt);
|
return callChatCompletions("Chart Data Extraction", prompt);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Mono<String> getChartSvg(String jsonData) {
|
public Mono<String> getChartSvg(String jsonData) {
|
||||||
|
// 3. Логируем параметры, с которыми вызывается метод
|
||||||
|
logger.info("Requesting SVG code for chart from OpenAI...");
|
||||||
|
logger.info("SVG generation request based on JSON: {}", jsonData); // DEBUG, чтобы не засорять логи
|
||||||
String prompt = buildSvgPrompt(jsonData);
|
String prompt = buildSvgPrompt(jsonData);
|
||||||
return callChatCompletions(prompt);
|
return callChatCompletions("SVG Generation", prompt);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Mono<String> callChatCompletions(String prompt) {
|
private Mono<String> callChatCompletions(String taskName, String prompt) {
|
||||||
Map<String, Object> body = new HashMap<>();
|
Map<String, Object> body = new HashMap<>();
|
||||||
body.put("model", modelName);
|
body.put("model", modelName);
|
||||||
body.put("temperature", 0);
|
body.put("temperature", 0);
|
||||||
body.put("messages", List.of(
|
body.put("messages", List.of(
|
||||||
Map.of("role", "user", "content", prompt)));
|
Map.of("role", "user", "content", prompt)));
|
||||||
|
|
||||||
|
logger.info("Calling OpenAI API for task '{}' with model '{}'.", taskName, modelName);
|
||||||
|
logger.info("OpenAI Prompt: {}", prompt);
|
||||||
|
|
||||||
return this.webClient.post()
|
return this.webClient.post()
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
.accept(MediaType.APPLICATION_JSON)
|
.accept(MediaType.APPLICATION_JSON)
|
||||||
@@ -61,21 +85,51 @@ public class OpenAiChartService {
|
|||||||
.bodyToMono(new ParameterizedTypeReference<Map<String, Object>>() {
|
.bodyToMono(new ParameterizedTypeReference<Map<String, Object>>() {
|
||||||
})
|
})
|
||||||
.timeout(Duration.ofMillis(timeoutMs))
|
.timeout(Duration.ofMillis(timeoutMs))
|
||||||
|
.doOnSuccess(response -> {
|
||||||
|
// 4. Логируем успешный, но еще не обработанный ответ
|
||||||
|
logger.info("Raw OpenAI response for task '{}': {}", taskName, response);
|
||||||
|
})
|
||||||
.map(resp -> {
|
.map(resp -> {
|
||||||
try {
|
try {
|
||||||
List<Map<String, Object>> choices = (List<Map<String, Object>>) resp.get("choices");
|
List<Map<String, Object>> choices = (List<Map<String, Object>>) resp.get("choices");
|
||||||
if (choices == null || choices.isEmpty())
|
if (choices == null || choices.isEmpty()) {
|
||||||
|
// 5. Логируем аномальные, но не ошибочные ситуации
|
||||||
|
logger.warn("OpenAI response for task '{}' contained no 'choices'.", taskName);
|
||||||
return null;
|
return null;
|
||||||
Map<String, Object> choice0 = choices.get(0);
|
}
|
||||||
Map<String, Object> message = (Map<String, Object>) choice0.get("message");
|
Map<String, Object> message = (Map<String, Object>) choices.get(0).get("message");
|
||||||
if (message == null)
|
if (message == null) {
|
||||||
|
logger.warn("OpenAI choice for task '{}' contained no 'message'.", taskName);
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
Object content = message.get("content");
|
Object content = message.get("content");
|
||||||
return content == null ? null : String.valueOf(content);
|
if (content == null) {
|
||||||
|
logger.warn("OpenAI message for task '{}' contained no 'content'.", taskName);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String responseContent = String.valueOf(content);
|
||||||
|
logger.info("Successfully extracted content from OpenAI for task '{}'. Content length: {}",
|
||||||
|
taskName, responseContent.length());
|
||||||
|
return responseContent;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
// 6. Логируем ошибку парсинга, если структура ответа неожиданная
|
||||||
|
logger.error("Failed to parse OpenAI response for task '{}'. Response body: {}", taskName, resp,
|
||||||
|
e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
.doOnError(e -> {
|
||||||
|
// 7. Логируем ошибки сети или HTTP-статусов (4xx, 5xx)
|
||||||
|
if (e instanceof WebClientResponseException) {
|
||||||
|
WebClientResponseException wcre = (WebClientResponseException) e;
|
||||||
|
logger.error("Error from OpenAI API for task '{}'. Status: {}, Body: {}",
|
||||||
|
taskName, wcre.getStatusCode(), wcre.getResponseBodyAsString(), e);
|
||||||
|
} else {
|
||||||
|
logger.error("Generic WebClient error for task '{}'.", taskName, e);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.onErrorResume(e -> Mono.empty()); // В случае любой ошибки возвращаем пустой результат, чтобы не
|
||||||
|
// прерывать цепочку вызовов
|
||||||
}
|
}
|
||||||
|
|
||||||
private String buildChartDataPrompt(String learningsAsString) {
|
private String buildChartDataPrompt(String learningsAsString) {
|
||||||
|
|||||||
Reference in New Issue
Block a user