diff --git a/pom.xml b/pom.xml
index f426642..7ba95c6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -42,8 +42,12 @@
spring-boot-starter-web
-
-
+
+ com.google.auth
+ google-auth-library-oauth2-http
+ 1.23.0
+
+
org.springframework.boot
spring-boot-starter-data-mongodb
diff --git a/src/main/java/kz/konturai/parser/service/NanoBananaImageGenerationService.java b/src/main/java/kz/konturai/parser/service/NanoBananaImageGenerationService.java
index dfe5599..97d130c 100644
--- a/src/main/java/kz/konturai/parser/service/NanoBananaImageGenerationService.java
+++ b/src/main/java/kz/konturai/parser/service/NanoBananaImageGenerationService.java
@@ -1,5 +1,8 @@
package kz.konturai.parser.service;
+import com.google.auth.oauth2.AccessToken;
+import com.google.auth.oauth2.GoogleCredentials;
+import io.netty.resolver.DefaultAddressResolverGroup;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
@@ -9,10 +12,11 @@ import org.springframework.stereotype.Service;
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.core.publisher.Mono;
import reactor.netty.http.client.HttpClient;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
+import java.io.IOException;
+import java.net.URI;
import java.time.Duration;
import java.util.ArrayList;
import java.util.HashMap;
@@ -24,15 +28,19 @@ import java.util.concurrent.Semaphore;
public class NanoBananaImageGenerationService implements ImageGenerationService {
private static final Logger logger = LoggerFactory.getLogger(NanoBananaImageGenerationService.class);
- // Базовый URL остался, но эндпоинты могут отличаться
- private static final String GEMINI_API_BASE_URL = "https://generativelanguage.googleapis.com/v1beta/models/";
+
+ // Шаблон URL для Vertex AI (не Generative Language!)
+ private static final String VERTEX_API_TEMPLATE = "https://%s-aiplatform.googleapis.com/v1/projects/%s/locations/%s/publishers/google/models/%s:predict";
private final Semaphore semaphore = new Semaphore(1);
private volatile long lastRequestTimestamp = 0;
private final WebClient webClient;
- @Value("${google.gemini.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.image.model:imagen-3.0-generate-001}")
private String model;
@@ -48,14 +56,15 @@ public class NanoBananaImageGenerationService implements ImageGenerationService
public NanoBananaImageGenerationService() {
HttpClient httpClient = HttpClient.create()
+ .resolver(DefaultAddressResolverGroup.INSTANCE)
.responseTimeout(Duration.ofMillis(120000));
+ // Увеличиваем буфер памяти для приема больших картинок (Base64)
ExchangeStrategies strategies = ExchangeStrategies.builder()
- .codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(15 * 1024 * 1024)) // Увеличим до 15MB
+ .codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(20 * 1024 * 1024)) // 20MB
.build();
this.webClient = WebClient.builder()
- .baseUrl(GEMINI_API_BASE_URL)
.clientConnector(new ReactorClientHttpConnector(httpClient))
.exchangeStrategies(strategies)
.build();
@@ -63,8 +72,8 @@ public class NanoBananaImageGenerationService implements ImageGenerationService
@Override
public byte[] generateImage(String prompt) {
- if (apiKey == null || apiKey.trim().isEmpty()) {
- logger.error("API Key is missing!");
+ if (projectId == null || projectId.trim().isEmpty()) {
+ logger.error("Project ID is missing! Check application.properties");
return null;
}
@@ -76,41 +85,63 @@ public class NanoBananaImageGenerationService implements ImageGenerationService
Thread.sleep(rateLimitDelayMs - timeSinceLast);
}
+ String accessToken = getAccessToken();
+ if (accessToken == null) {
+ logger.error("Failed to get Access Token. Check 'gcloud auth login' or JSON key file.");
+ return null;
+ }
+
+ // 2. Формируем URL для Vertex AI
+ // Пример: https://us-central1-aiplatform.googleapis.com/...
+ String endpointUrl = String.format(VERTEX_API_TEMPLATE, location, projectId, location, model);
+
String enrichedPrompt = enrichPromptForGemini(prompt);
-
- String endpoint = model + ":predict";
-
Map requestBody = buildImagenRequestBody(enrichedPrompt);
- logger.info("Sending request to Imagen model: {}", model);
+ logger.info("Sending request to Vertex AI. Project: {}, Model: {}", projectId, model);
+ // 3. Выполняем POST запрос
Map response = webClient.post()
- .uri(uriBuilder -> uriBuilder
- .path(endpoint)
- .queryParam("key", apiKey)
- .build())
+ .uri(URI.create(endpointUrl))
+ .header("Authorization", "Bearer " + accessToken) // Авторизация через токен
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(requestBody)
.retrieve()
.bodyToMono(new ParameterizedTypeReference