This commit is contained in:
root
2025-12-27 20:31:38 +05:00
parent febc216c1e
commit dced51168c
@@ -27,11 +27,15 @@ import java.util.*;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
@Service @Service
public class MarketingAnalysisService { public class MarketingAnalysisService {
private final Semaphore openAiSemaphore = new Semaphore(3); // Разрешить только 3 активных запроса к OpenAI на всё
// приложение
private static final String AI_MANDATORY_JSON_USAGE_WARNING = "ВНИМАНИЕ: Если в предоставленном JSON есть данные, ты ОБЯЗАН использовать их и вставить соответствующий тег [[CHART_...]]. " private static final String AI_MANDATORY_JSON_USAGE_WARNING = "ВНИМАНИЕ: Если в предоставленном JSON есть данные, ты ОБЯЗАН использовать их и вставить соответствующий тег [[CHART_...]]. "
+ "Никогда не пиши, что данных нет, если JSON не пустой.\n\n"; + "Никогда не пиши, что данных нет, если JSON не пустой.\n\n";
private static final String AI_ULTIMATE_JSON_PRESENCE_INSTRUCTION = "" private static final String AI_ULTIMATE_JSON_PRESENCE_INSTRUCTION = ""
@@ -1556,42 +1560,52 @@ public class MarketingAnalysisService {
String fallbackModel, String fallbackModel,
int attempts, int attempts,
String sectionId) { String sectionId) {
int maxAttempts = Math.max(1, attempts); try {
String last = null;
DetailLevel detailLevel = resolveDetailLevelFromContext(contextJson);
String systemPrompt = buildSystemPromptForSection(detailLevel, sectionId);
Integer maxTokensOverride = detailLevel == DetailLevel.ПОДРОБНО ? REPORT_BASE_MAX_TOKENS * 2 : null;
for (int i = 1; i <= maxAttempts; i++) { openAiSemaphore.acquire();
String modelToUse = primaryModel; int maxAttempts = Math.max(1, attempts);
// На последней попытке можно переключиться на запасную модель (если задана) String last = null;
if (i == maxAttempts && fallbackModel != null && !fallbackModel.isBlank()) { DetailLevel detailLevel = resolveDetailLevelFromContext(contextJson);
modelToUse = fallbackModel; String systemPrompt = buildSystemPromptForSection(detailLevel, sectionId);
} Integer maxTokensOverride = detailLevel == DetailLevel.ПОДРОБНО ? REPORT_BASE_MAX_TOKENS * 2 : null;
try {
last = openAIAnalyticsService.generateWithInstructionWithModel(
jsonContext, prompt, lang, modelToUse, systemPrompt, maxTokensOverride);
} catch (Exception e) {
logger.warn("generateWithRetry: attempt {} failed with exception: {}", i, e.getMessage());
last = null;
}
if (isValidAiResponse(last)) { for (int i = 1; i <= maxAttempts; i++) {
return last; String modelToUse = primaryModel;
} // На последней попытке можно переключиться на запасную модель (если задана)
if (i == maxAttempts && fallbackModel != null && !fallbackModel.isBlank()) {
// Небольшая пауза между попытками, чтобы сгладить кратковременные сбои modelToUse = fallbackModel;
if (i < maxAttempts) { }
try { try {
Thread.sleep(250L); last = openAIAnalyticsService.generateWithInstructionWithModel(
} catch (InterruptedException ie) { jsonContext, prompt, lang, modelToUse, systemPrompt, maxTokensOverride);
Thread.currentThread().interrupt(); } catch (Exception e) {
break; logger.warn("generateWithRetry: attempt {} failed with exception: {}", i, e.getMessage());
last = null;
}
if (isValidAiResponse(last)) {
return last;
}
// Небольшая пауза между попытками, чтобы сгладить кратковременные сбои
if (i < maxAttempts) {
try {
Thread.sleep(250L);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
break;
}
} }
} }
}
return last; return last;
} catch (Exception e) {
logger.error("generateWithRetryForSection: Error generating section: {}", e.getMessage(), e);
logger.error("generateWithRetryForSection: Stack trace: ", e);
return null;
} finally {
openAiSemaphore.release();
}
} }
// Backward-compatible helper (legacy signature). Kept because file historically // Backward-compatible helper (legacy signature). Kept because file historically