fix
This commit is contained in:
@@ -1,86 +1,128 @@
|
||||
package kz.konturai.parser.service;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import com.google.auth.oauth2.GoogleCredentials;
|
||||
import io.netty.resolver.DefaultAddressResolverGroup;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.*;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.reactive.function.client.ExchangeStrategies;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import org.springframework.web.reactive.function.client.WebClientResponseException;
|
||||
import reactor.netty.http.client.HttpClient;
|
||||
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
|
||||
|
||||
import java.util.Base64;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.time.Duration;
|
||||
import java.util.*;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class GeminiVideoGenerationService {
|
||||
|
||||
private final RestTemplate restTemplate = new RestTemplate();
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
private static final String VERTEX_API_TEMPLATE = "https://%s-aiplatform.googleapis.com/v1/projects/%s/locations/%s/publishers/google/models/%s:predict";
|
||||
private static final String CREDENTIALS_FILE_PATH = "keys/google-key.json";
|
||||
|
||||
@Value("${gemini.veo.api.url:https://generativelanguage.googleapis.com/v1beta/models/veo:generateVideo}")
|
||||
private String videoApiUrl;
|
||||
private final WebClient webClient;
|
||||
|
||||
@Value("${gemini.veo.api.key:YOUR_API_KEY}")
|
||||
private String apiKey;
|
||||
@Value("${google.cloud.project-id}")
|
||||
private String projectId;
|
||||
|
||||
@Value("${google.cloud.location:us-central1}")
|
||||
private String location;
|
||||
|
||||
@Value("${google.gemini.video.model:veo-2.0-generate-001}")
|
||||
private String model;
|
||||
|
||||
public GeminiVideoGenerationService() {
|
||||
HttpClient httpClient = HttpClient.create()
|
||||
.resolver(DefaultAddressResolverGroup.INSTANCE)
|
||||
.responseTimeout(Duration.ofMillis(300000));
|
||||
|
||||
ExchangeStrategies strategies = ExchangeStrategies.builder()
|
||||
.codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(100 * 1024 * 1024))
|
||||
.build();
|
||||
|
||||
this.webClient = WebClient.builder()
|
||||
.clientConnector(new ReactorClientHttpConnector(httpClient))
|
||||
.exchangeStrategies(strategies)
|
||||
.build();
|
||||
}
|
||||
|
||||
public byte[] generateVideo(String prompt) {
|
||||
if (projectId == null || projectId.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
headers.set("x-goog-api-key", apiKey);
|
||||
|
||||
Map<String, Object> requestBody = new HashMap<>();
|
||||
requestBody.put("prompt", prompt);
|
||||
requestBody.put("aspectRatio", "9:16");
|
||||
requestBody.put("resolution", "1080p");
|
||||
|
||||
HttpEntity<Map<String, Object>> entity = new HttpEntity<>(requestBody, headers);
|
||||
|
||||
ResponseEntity<String> response = restTemplate.exchange(
|
||||
videoApiUrl,
|
||||
HttpMethod.POST,
|
||||
entity,
|
||||
String.class
|
||||
);
|
||||
|
||||
if (response.getStatusCode() == HttpStatus.OK && response.getBody() != null) {
|
||||
String jsonBody = response.getBody();
|
||||
JsonNode rootNode = objectMapper.readTree(jsonBody);
|
||||
String base64Video = null;
|
||||
|
||||
if (rootNode.has("predictions") && rootNode.get("predictions").isArray()) {
|
||||
JsonNode prediction = rootNode.get("predictions").get(0);
|
||||
if (prediction.has("bytesBase64Encoded")) {
|
||||
base64Video = prediction.get("bytesBase64Encoded").asText();
|
||||
}
|
||||
} else if (rootNode.has("candidates") && rootNode.get("candidates").isArray()) {
|
||||
JsonNode parts = rootNode.at("/candidates/0/content/parts");
|
||||
if (parts.isArray() && parts.size() > 0 && parts.get(0).has("inlineData")) {
|
||||
base64Video = parts.get(0).at("/inlineData/data").asText();
|
||||
}
|
||||
} else if (rootNode.has("videoBase64")) {
|
||||
base64Video = rootNode.get("videoBase64").asText();
|
||||
} else if (rootNode.has("base64")) {
|
||||
base64Video = rootNode.get("base64").asText();
|
||||
}
|
||||
|
||||
if (base64Video != null && !base64Video.isEmpty()) {
|
||||
return Base64.getDecoder().decode(base64Video);
|
||||
} else {
|
||||
if (!jsonBody.trim().startsWith("{")) {
|
||||
return jsonBody.getBytes(java.nio.charset.StandardCharsets.ISO_8859_1);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
String accessToken = getAccessTokenFromResources();
|
||||
if (accessToken == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String endpointUrl = String.format(VERTEX_API_TEMPLATE, location, projectId, location, model);
|
||||
|
||||
Map<String, Object> requestBody = new HashMap<>();
|
||||
List<Map<String, Object>> instances = new ArrayList<>();
|
||||
Map<String, Object> instance = new HashMap<>();
|
||||
instance.put("prompt", prompt);
|
||||
instances.add(instance);
|
||||
requestBody.put("instances", instances);
|
||||
|
||||
Map<String, Object> parameters = new HashMap<>();
|
||||
parameters.put("aspectRatio", "9:16");
|
||||
requestBody.put("parameters", parameters);
|
||||
|
||||
Map<String, Object> response = webClient.post()
|
||||
.uri(URI.create(endpointUrl))
|
||||
.header("Authorization", "Bearer " + accessToken)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(requestBody)
|
||||
.retrieve()
|
||||
.bodyToMono(new ParameterizedTypeReference<Map<String, Object>>() {})
|
||||
.retryWhen(reactor.util.retry.Retry.backoff(3, Duration.ofSeconds(15))
|
||||
.filter(t -> t instanceof WebClientResponseException &&
|
||||
((WebClientResponseException) t).getStatusCode().value() == 429))
|
||||
.block(Duration.ofMillis(300000));
|
||||
|
||||
return extractVideoFromResponse(response);
|
||||
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String getAccessTokenFromResources() {
|
||||
try {
|
||||
ClassPathResource resource = new ClassPathResource(CREDENTIALS_FILE_PATH);
|
||||
if (!resource.exists()) return null;
|
||||
try (InputStream is = resource.getInputStream()) {
|
||||
GoogleCredentials credentials = GoogleCredentials.fromStream(is)
|
||||
.createScoped("https://www.googleapis.com/auth/cloud-platform");
|
||||
credentials.refreshIfExpired();
|
||||
return credentials.getAccessToken().getTokenValue();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private byte[] extractVideoFromResponse(Map<String, Object> response) {
|
||||
if (response == null) return null;
|
||||
try {
|
||||
List<Map<String, Object>> predictions = (List<Map<String, Object>>) response.get("predictions");
|
||||
if (predictions != null && !predictions.isEmpty()) {
|
||||
Map<String, Object> firstPrediction = predictions.get(0);
|
||||
String base64Video = (String) firstPrediction.get("bytesBase64Encoded");
|
||||
if (base64Video != null) {
|
||||
return Base64.getDecoder().decode(base64Video);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,7 @@ public class MarketingStrategyV3Service {
|
||||
@Value("${openai.model.name.text:gpt-4o}")
|
||||
private String highIntelligenceModel;
|
||||
|
||||
@Value("${image.generation.delayBetweenRequestsMs:5000}") // Увеличили задержку до 5 сек
|
||||
@Value("${image.generation.delayBetweenRequestsMs:5000}")
|
||||
private long delayBetweenRequestsMs;
|
||||
|
||||
public Optional<MarketingStrategy> getStrategyById(String id) {
|
||||
@@ -204,8 +204,6 @@ public class MarketingStrategyV3Service {
|
||||
}
|
||||
|
||||
MarketingStrategy.PostCalendarItem item = postCalendar.get(postIndex);
|
||||
|
||||
// Убеждаемся, что тип контента фото, если регенерируем фото
|
||||
item.setContentType("фото");
|
||||
|
||||
Optional<MarketingAnalysisV3Document> analysisOpt = analysisRepository.findById(strategy.getAnalysisId());
|
||||
@@ -237,7 +235,6 @@ public class MarketingStrategyV3Service {
|
||||
return item;
|
||||
}
|
||||
|
||||
// НОВЫЙ МЕТОД ДЛЯ РЕГЕНЕРАЦИИ ВИДЕО
|
||||
public MarketingStrategy.PostCalendarItem regeneratePostVideo(String strategyId, int postIndex) {
|
||||
Optional<MarketingStrategy> optStrategy = repository.findById(strategyId);
|
||||
if (optStrategy.isEmpty()) {
|
||||
@@ -252,8 +249,6 @@ public class MarketingStrategyV3Service {
|
||||
}
|
||||
|
||||
MarketingStrategy.PostCalendarItem item = postCalendar.get(postIndex);
|
||||
|
||||
// Меняем тип контента на видео
|
||||
item.setContentType("видео");
|
||||
|
||||
Optional<MarketingAnalysisV3Document> analysisOpt = analysisRepository.findById(strategy.getAnalysisId());
|
||||
@@ -266,7 +261,6 @@ public class MarketingStrategyV3Service {
|
||||
|
||||
doGenerateVideo(item, businessContext);
|
||||
|
||||
// Если видео не сгенерировалось (фоллбек на фото), сохраняем изменения
|
||||
repository.save(strategy);
|
||||
return item;
|
||||
}
|
||||
@@ -403,7 +397,7 @@ public class MarketingStrategyV3Service {
|
||||
|
||||
try {
|
||||
if (contentType.contains("видео") || contentType.contains("reels") || contentType.contains("tiktok")) {
|
||||
doGenerateVideoWithFallback(item, businessContext, brandName, currentRefBytes);
|
||||
doGenerateVideo(item, businessContext);
|
||||
} else {
|
||||
doGenerateImage(item, businessContext, brandName, currentRefBytes);
|
||||
}
|
||||
@@ -416,30 +410,6 @@ public class MarketingStrategyV3Service {
|
||||
}
|
||||
}
|
||||
|
||||
// Выделенный метод для генерации видео с фоллбеком
|
||||
private void doGenerateVideoWithFallback(MarketingStrategy.PostCalendarItem item, String businessContext, String brandName, byte[] currentRefBytes) {
|
||||
String videoPrompt = "High quality cinematic commercial video. Business niche: " + businessContext + ". Scene: " + item.getTheme() + ". Photorealistic, dynamic motion, 4k.";
|
||||
|
||||
byte[] videoBytes = null;
|
||||
try {
|
||||
videoBytes = geminiVideoService.generateVideo(videoPrompt);
|
||||
} catch (Exception e) {
|
||||
log.warn("Video service failed: {}", e.getMessage());
|
||||
}
|
||||
|
||||
if (videoBytes != null && videoBytes.length > 0) {
|
||||
String filename = "video_" + System.currentTimeMillis() + "_" + item.hashCode() + ".mp4";
|
||||
minIOService.uploadFile(filename, videoBytes, "video/mp4");
|
||||
item.setVideoUrl(filename);
|
||||
item.setVideoFilename(filename);
|
||||
} else {
|
||||
log.warn("Fallback: Video generation failed. Switching to Image generation for theme: {}", item.getTheme());
|
||||
item.setContentType("фото");
|
||||
doGenerateImage(item, businessContext, brandName, currentRefBytes);
|
||||
}
|
||||
}
|
||||
|
||||
// Выделенный метод генерации видео БЕЗ фоллбека (для ручной регенерации)
|
||||
private void doGenerateVideo(MarketingStrategy.PostCalendarItem item, String businessContext) {
|
||||
String videoPrompt = "High quality cinematic commercial video. Business niche: " + businessContext + ". Scene: " + item.getTheme() + ". Photorealistic, dynamic motion, 4k.";
|
||||
try {
|
||||
@@ -449,14 +419,17 @@ public class MarketingStrategyV3Service {
|
||||
minIOService.uploadFile(filename, videoBytes, "video/mp4");
|
||||
item.setVideoUrl(filename);
|
||||
item.setVideoFilename(filename);
|
||||
// Очищаем данные фото, так как теперь это видео
|
||||
item.setImageUrl(null);
|
||||
item.setImageFilename(null);
|
||||
} else {
|
||||
log.error("Failed to regenerate video, returned null bytes");
|
||||
log.error("Video generation returned null for theme: {}", item.getTheme());
|
||||
item.setVideoFilename(null);
|
||||
item.setVideoUrl(null);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Error regenerating video: {}", e.getMessage());
|
||||
log.error("Error generating video: {}", e.getMessage());
|
||||
item.setVideoFilename(null);
|
||||
item.setVideoUrl(null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -469,12 +442,17 @@ public class MarketingStrategyV3Service {
|
||||
minIOService.uploadFile(filename, imageBytes, MediaType.IMAGE_PNG_VALUE);
|
||||
item.setImageUrl(filename);
|
||||
item.setImageFilename(filename);
|
||||
// Очищаем данные видео, так как теперь это фото
|
||||
item.setVideoUrl(null);
|
||||
item.setVideoFilename(null);
|
||||
} else {
|
||||
log.error("Image generation returned null for theme: {}", item.getTheme());
|
||||
item.setImageFilename(null);
|
||||
item.setImageUrl(null);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Image generation failed: {}", e.getMessage());
|
||||
log.error("Image generation crashed: {}", e.getMessage());
|
||||
item.setImageFilename(null);
|
||||
item.setImageUrl(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,10 +46,10 @@ public class NanoBananaImageGenerationService implements ImageGenerationService
|
||||
@Value("${google.gemini.timeoutMs:120000}")
|
||||
private long timeoutMs;
|
||||
|
||||
@Value("${google.gemini.retry.maxAttempts:3}")
|
||||
@Value("${google.gemini.retry.maxAttempts:5}")
|
||||
private int maxRetryAttempts;
|
||||
|
||||
@Value("${google.gemini.rateLimit.delayMs:12000}")
|
||||
@Value("${google.gemini.rateLimit.delayMs:15000}")
|
||||
private long rateLimitDelayMs;
|
||||
|
||||
public NanoBananaImageGenerationService() {
|
||||
@@ -79,14 +79,12 @@ public class NanoBananaImageGenerationService implements ImageGenerationService
|
||||
try {
|
||||
return generateImageWithReference(prompt, referenceLogo.getBytes());
|
||||
} catch (IOException e) {
|
||||
logger.error("Error reading MultipartFile logo: {}", e.getMessage());
|
||||
return generateImage(prompt);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] generateImageWithReference(String prompt, byte[] referenceImageBytes) {
|
||||
if (projectId == null || projectId.trim().isEmpty()) {
|
||||
logger.error("Project ID is missing! Check application.properties");
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -104,7 +102,7 @@ public class NanoBananaImageGenerationService implements ImageGenerationService
|
||||
}
|
||||
|
||||
String endpointUrl = String.format(VERTEX_API_TEMPLATE, location, projectId, location, model);
|
||||
String finalPrompt = sanitizePromptForText(prompt);
|
||||
String finalPrompt = prompt != null ? prompt : "High quality image";
|
||||
|
||||
try {
|
||||
Map<String, Object> requestBody = buildImagenRequestBody(finalPrompt, referenceImageBytes);
|
||||
@@ -113,18 +111,17 @@ public class NanoBananaImageGenerationService implements ImageGenerationService
|
||||
return extractImageFromImagenResponse(response);
|
||||
} catch (WebClientResponseException.BadRequest badRequestEx) {
|
||||
if (referenceImageBytes != null && referenceImageBytes.length > 0) {
|
||||
logger.warn("Vertex AI rejected the request with reference image (400 Bad Request). Retrying WITHOUT reference image...");
|
||||
Map<String, Object> fallbackBody = buildImagenRequestBody(finalPrompt, null);
|
||||
Map<String, Object> fallbackResponse = executeVertexRequest(endpointUrl, accessToken, fallbackBody);
|
||||
lastRequestTimestamp = System.currentTimeMillis();
|
||||
return extractImageFromImagenResponse(fallbackResponse);
|
||||
} else {
|
||||
throw badRequestEx;
|
||||
}
|
||||
return null;
|
||||
} catch (Exception ex) {
|
||||
return null;
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("Fatal error in NanoBanana (Vertex AI): {}", e.getMessage(), e);
|
||||
return null;
|
||||
} finally {
|
||||
semaphore.release();
|
||||
@@ -132,15 +129,27 @@ public class NanoBananaImageGenerationService implements ImageGenerationService
|
||||
}
|
||||
|
||||
private Map<String, Object> executeVertexRequest(String endpointUrl, String accessToken, Map<String, Object> requestBody) {
|
||||
return webClient.post()
|
||||
.uri(URI.create(endpointUrl))
|
||||
.header("Authorization", "Bearer " + accessToken)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(requestBody)
|
||||
.retrieve()
|
||||
.bodyToMono(new ParameterizedTypeReference<Map<String, Object>>() {})
|
||||
.retryWhen(createRetrySpecFor429())
|
||||
.block(Duration.ofMillis(timeoutMs));
|
||||
try {
|
||||
return webClient.post()
|
||||
.uri(URI.create(endpointUrl))
|
||||
.header("Authorization", "Bearer " + accessToken)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(requestBody)
|
||||
.retrieve()
|
||||
.bodyToMono(new ParameterizedTypeReference<Map<String, Object>>() {})
|
||||
.retryWhen(reactor.util.retry.Retry.backoff(maxRetryAttempts, Duration.ofSeconds(10))
|
||||
.maxBackoff(Duration.ofSeconds(60))
|
||||
.filter(t -> t instanceof WebClientResponseException &&
|
||||
((WebClientResponseException) t).getStatusCode().value() == 429))
|
||||
.block(Duration.ofMillis(timeoutMs));
|
||||
} catch (WebClientResponseException e) {
|
||||
if (e.getStatusCode().value() == 400) {
|
||||
throw e;
|
||||
}
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String getAccessTokenFromResources() {
|
||||
@@ -149,7 +158,6 @@ public class NanoBananaImageGenerationService implements ImageGenerationService
|
||||
if (!resource.exists()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try (InputStream is = resource.getInputStream()) {
|
||||
GoogleCredentials credentials = GoogleCredentials.fromStream(is)
|
||||
.createScoped("https://www.googleapis.com/auth/cloud-platform");
|
||||
@@ -161,11 +169,6 @@ public class NanoBananaImageGenerationService implements ImageGenerationService
|
||||
}
|
||||
}
|
||||
|
||||
private String sanitizePromptForText(String originalPrompt) {
|
||||
if (originalPrompt == null) return "High quality, photorealistic image";
|
||||
return originalPrompt; // Убрали лишнюю очистку, которая могла ломать логику ИИ
|
||||
}
|
||||
|
||||
private Map<String, Object> buildImagenRequestBody(String prompt, byte[] referenceImageBytes) {
|
||||
Map<String, Object> body = new HashMap<>();
|
||||
List<Map<String, Object>> instances = new ArrayList<>();
|
||||
@@ -175,22 +178,18 @@ public class NanoBananaImageGenerationService implements ImageGenerationService
|
||||
|
||||
if (referenceImageBytes != null && referenceImageBytes.length > 0) {
|
||||
String base64Image = Base64.getEncoder().encodeToString(referenceImageBytes);
|
||||
|
||||
Map<String, Object> referenceImageParams = new HashMap<>();
|
||||
|
||||
Map<String, Object> imageBytesMap = new HashMap<>();
|
||||
imageBytesMap.put("bytesBase64Encoded", base64Image);
|
||||
// Убрали mimeType, чтобы избежать 400 Bad Request из-за несоответствия типов
|
||||
|
||||
imageBytesMap.put("bytesBase64Encoded", base64Image);
|
||||
referenceImageParams.put("referenceImage", imageBytesMap);
|
||||
// Возвращаем STYLE, так как он меньше ломает геометрию
|
||||
referenceImageParams.put("referenceType", "STYLE");
|
||||
referenceImageParams.put("referenceType", "SUBJECT");
|
||||
|
||||
List<Map<String, Object>> refImagesList = new ArrayList<>();
|
||||
refImagesList.add(referenceImageParams);
|
||||
|
||||
instance.put("referenceImages", refImagesList);
|
||||
instance.put("prompt", prompt + ". Use the provided reference image.");
|
||||
instance.put("prompt", prompt + ". Naturally integrate the provided reference subject into the scene.");
|
||||
}
|
||||
|
||||
instances.add(instance);
|
||||
@@ -201,8 +200,7 @@ public class NanoBananaImageGenerationService implements ImageGenerationService
|
||||
parameters.put("aspectRatio", "1:1");
|
||||
parameters.put("safetyFilterLevel", "block_some");
|
||||
parameters.put("personGeneration", "allow_adult");
|
||||
// Убрали text из негативного промпта
|
||||
parameters.put("negativePrompt", "nsfw, nudity, violence, deformed, ugly, blurry, distorted");
|
||||
parameters.put("negativePrompt", "text, typography, watermark, signature, blurry, distorted, bad anatomy");
|
||||
|
||||
body.put("parameters", parameters);
|
||||
return body;
|
||||
@@ -224,15 +222,7 @@ public class NanoBananaImageGenerationService implements ImageGenerationService
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("Parsing error: {}", e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private reactor.util.retry.RetryBackoffSpec createRetrySpecFor429() {
|
||||
return reactor.util.retry.Retry.backoff(maxRetryAttempts, Duration.ofSeconds(5))
|
||||
.maxBackoff(Duration.ofSeconds(30))
|
||||
.filter(t -> t instanceof WebClientResponseException &&
|
||||
((WebClientResponseException) t).getStatusCode().value() == 429);
|
||||
}
|
||||
}
|
||||
@@ -3,17 +3,15 @@ ollama.model=gemma3:1b
|
||||
ollama.timeoutMs=18000000
|
||||
ollama.connectTimeoutMs=10000000
|
||||
ollama.responseTimeoutMs=18000000
|
||||
# MinIO configuration
|
||||
|
||||
minio.endpoint=http://92.38.48.166:9000
|
||||
minio.access-key=z9HhZTjzhVWqw3D20u7q
|
||||
minio.secret-key=lhhHtJow1b10z6EZRMTQIU1tsKkoaxCVpSypCrcb
|
||||
minio.bucket-name=konturai
|
||||
|
||||
# Headless mode for chart generation in Docker
|
||||
java.awt.headless=true
|
||||
spring.application.name=parser
|
||||
|
||||
# MongoDB Configuration
|
||||
spring.data.mongodb.host=92.38.48.166
|
||||
spring.data.mongodb.port=27017
|
||||
spring.data.mongodb.database=parser_db
|
||||
@@ -21,24 +19,20 @@ spring.data.mongodb.username=konturai
|
||||
spring.data.mongodb.password=konturai2024
|
||||
spring.data.mongodb.authentication-database=admin
|
||||
|
||||
# RSS Feed URLs
|
||||
rss.feed.url=https://kursiv.media/feed/
|
||||
rss.kapital.url=https://kapital.kz/rss/
|
||||
rss.lsm.url=https://lsm.kz/rss
|
||||
rss.rbc.url=https://static.feed.rbc.ru/rbc/logical/footer/news.rss
|
||||
rss.vedomosti.url=https://www.vedomosti.ru/rss/rubric/technology/internet
|
||||
|
||||
# Scheduler Configuration
|
||||
parser.scheduler.enabled=false
|
||||
spring.task.scheduling.pool.size=5
|
||||
spring.task.scheduling.thread-name-prefix=scheduled-task-
|
||||
|
||||
# MongoDB Connection Settings
|
||||
spring.data.mongodb.connection-timeout=30000
|
||||
spring.data.mongodb.socket-timeout=30000
|
||||
spring.data.mongodb.server-selection-timeout=30000
|
||||
|
||||
# Logging Configuration
|
||||
logging.level.kz.konturai.parser.service.KursivParserService=INFO
|
||||
logging.level.kz.konturai.parser.service.KapitalParserService=INFO
|
||||
logging.level.kz.konturai.parser.service.LsmParserService=INFO
|
||||
@@ -53,7 +47,6 @@ logging.level.kz.konturai.parser.service.MarketingAnalysisService=DEBUG
|
||||
logging.level.kz.konturai.parser.config.MongoConfig=DEBUG
|
||||
logging.level.org.springframework.data.mongodb.core.convert=DEBUG
|
||||
|
||||
# OpenAI Configuration
|
||||
openai.api.key=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
|
||||
openai.api.url=https://api.openai.com/v1/chat/completions
|
||||
openai.model.name=gpt-4o-mini
|
||||
@@ -69,7 +62,6 @@ openai.retry.rateLimitInitialDelayMs=5000
|
||||
openai.retry.rateLimitMaxDelayMs=300000
|
||||
openai.rateLimit.maxConcurrentRequests=3
|
||||
|
||||
# Serper.dev Web Search Configuration
|
||||
serper.api.key=5ae7ad0baf7dfacb8bcd5fc8dca01f61b3207e75
|
||||
serper.api.url=https://google.serper.dev
|
||||
serper.timeoutMs=30000
|
||||
@@ -78,27 +70,25 @@ serper.retry.initialDelayMs=1000
|
||||
serper.retry.maxDelayMs=10000
|
||||
serper.retry.multiplier=2.0
|
||||
|
||||
# OpenAI DALL-E Image Generation Configuration
|
||||
openai.image.model=dall-e-3
|
||||
openai.image.size=1024x1024
|
||||
openai.image.quality=hd
|
||||
openai.image.style=natural
|
||||
|
||||
# Image Generation Provider Selection
|
||||
image.generation.provider=nano-banana
|
||||
image.generation.delayBetweenRequestsMs=3000
|
||||
|
||||
google.cloud.project-id=data-totality-483616-t5
|
||||
google.cloud.location=us-central1
|
||||
google.gemini.image.model=imagen-3.0-generate-001
|
||||
google.gemini.video.model=veo-2.0-generate-001
|
||||
google.gemini.timeoutMs=120000
|
||||
google.gemini.retry.maxAttempts=3
|
||||
google.gemini.retry.maxAttempts=5
|
||||
google.gemini.retry.initialDelayMs=10000
|
||||
google.gemini.retry.maxDelayMs=120000
|
||||
google.gemini.retry.multiplier=2.0
|
||||
google.gemini.rateLimit.delayMs=12000
|
||||
google.gemini.rateLimit.delayMs=20000
|
||||
|
||||
# Email Configuration
|
||||
spring.mail.host=smtp.gmail.com
|
||||
spring.mail.port=587
|
||||
spring.mail.username=your-email@gmail.com
|
||||
@@ -107,29 +97,17 @@ spring.mail.properties.mail.smtp.auth=true
|
||||
spring.mail.properties.mail.smtp.starttls.enable=true
|
||||
spring.mail.properties.mail.smtp.starttls.required=true
|
||||
|
||||
# Deep Research API Configuration
|
||||
deep-research.api.url=http://185.35.223.45:3051
|
||||
deep-research.api.timeout=1800000
|
||||
|
||||
# JWT Configuration
|
||||
security.jwt.secret-base64=ZmFrZV9zZWNyZXRfMTIzNDU2Nzg5MGFiY2RlZmFrZV9zZWNyZXRfMTIzNDU2Nzg5MGFiY2Rl
|
||||
|
||||
# Encryption Configuration
|
||||
encryption.secret-key=ZmFrZV9zZWNyZXRfMTIzNDU2Nzg5MGFiY2RlZmFrZV9zZWNyZXRfMTIzNDU2Nzg5MGFiY2Rl
|
||||
|
||||
# Posting Scheduler Configuration
|
||||
posting.scheduler.enabled=true
|
||||
|
||||
# Telegram API Configuration
|
||||
telegram.api.timeout=30000
|
||||
|
||||
# ==========================================
|
||||
# FACEBOOK MESSENGER CONFIGURATION
|
||||
# ==========================================
|
||||
facebook.api.timeout=30000
|
||||
|
||||
facebook.verify.token=konturAI
|
||||
|
||||
# ???? ?????? ??????? ????? ???????? (Page Access Token)
|
||||
# ?????? ??? ? Meta Developers -> Messenger -> Settings -> Access Tokens (Generate Token)
|
||||
facebook.page.access.token=EAAaocqgT3JoBQtNeU4uqBrywSRwGmFZBQDZBnd1ynWjX070wAa09QBKebjrd9vyjAZCiJuZAZCU8266VlJYTAfwue20QNvENgM3wpjWNmCrTO0gTMAkmPZBKZBXKvZA8eCA4aZCUhmU2jsBRluYqKUyqpYV3ZCK928OVqLqCNZCUhqU6nUU0xP0DOzk90x58tmxpS2y1i7hjQZDZD
|
||||
Reference in New Issue
Block a user