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.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@Service
public class MarketingAnalysisService {
private final Semaphore openAiSemaphore = new Semaphore(3); // Разрешить только 3 активных запроса к OpenAI на всё
// приложение
private static final String AI_MANDATORY_JSON_USAGE_WARNING = "ВНИМАНИЕ: Если в предоставленном JSON есть данные, ты ОБЯЗАН использовать их и вставить соответствующий тег [[CHART_...]]. "
+ "Никогда не пиши, что данных нет, если JSON не пустой.\n\n";
private static final String AI_ULTIMATE_JSON_PRESENCE_INSTRUCTION = ""
@@ -1556,42 +1560,52 @@ public class MarketingAnalysisService {
String fallbackModel,
int attempts,
String sectionId) {
int maxAttempts = Math.max(1, attempts);
String last = null;
DetailLevel detailLevel = resolveDetailLevelFromContext(contextJson);
String systemPrompt = buildSystemPromptForSection(detailLevel, sectionId);
Integer maxTokensOverride = detailLevel == DetailLevel.ПОДРОБНО ? REPORT_BASE_MAX_TOKENS * 2 : null;
try {
for (int i = 1; i <= maxAttempts; i++) {
String modelToUse = primaryModel;
// На последней попытке можно переключиться на запасную модель (если задана)
if (i == maxAttempts && fallbackModel != null && !fallbackModel.isBlank()) {
modelToUse = fallbackModel;
}
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;
}
openAiSemaphore.acquire();
int maxAttempts = Math.max(1, attempts);
String last = null;
DetailLevel detailLevel = resolveDetailLevelFromContext(contextJson);
String systemPrompt = buildSystemPromptForSection(detailLevel, sectionId);
Integer maxTokensOverride = detailLevel == DetailLevel.ПОДРОБНО ? REPORT_BASE_MAX_TOKENS * 2 : null;
if (isValidAiResponse(last)) {
return last;
}
// Небольшая пауза между попытками, чтобы сгладить кратковременные сбои
if (i < maxAttempts) {
for (int i = 1; i <= maxAttempts; i++) {
String modelToUse = primaryModel;
// На последней попытке можно переключиться на запасную модель (если задана)
if (i == maxAttempts && fallbackModel != null && !fallbackModel.isBlank()) {
modelToUse = fallbackModel;
}
try {
Thread.sleep(250L);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
break;
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)) {
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