diff --git a/spring-ai-4/pom.xml b/spring-ai-4/pom.xml
index ac2466af39f8..02e0aa659c99 100644
--- a/spring-ai-4/pom.xml
+++ b/spring-ai-4/pom.xml
@@ -61,6 +61,14 @@
org.springframework.ai
spring-ai-starter-model-openai
+
+ org.springframework.ai
+ spring-ai-starter-model-vertex-ai-gemini
+
+
+ org.springframework.ai
+ spring-ai-starter-model-vertex-ai-embedding
+
org.springframework.ai
spring-ai-model-chat-memory-repository-jdbc
@@ -89,6 +97,12 @@
com.baeldung.springai.memory.Application
+
+ vertexai
+
+ com.baeldung.springai.vertexai.Application
+
+
@@ -113,7 +127,7 @@
5.9.0
3.5.0
- 1.0.0
+ 1.0.1
diff --git a/spring-ai-4/src/main/java/com/baeldung/springai/memory/Application.java b/spring-ai-4/src/main/java/com/baeldung/springai/memory/Application.java
index 5cdaa360c6bc..ab9e2bbe6ecd 100644
--- a/spring-ai-4/src/main/java/com/baeldung/springai/memory/Application.java
+++ b/spring-ai-4/src/main/java/com/baeldung/springai/memory/Application.java
@@ -3,7 +3,11 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
-@SpringBootApplication
+@SpringBootApplication(exclude = {
+ org.springframework.ai.model.vertexai.autoconfigure.embedding.VertexAiMultiModalEmbeddingAutoConfiguration.class,
+ org.springframework.ai.model.vertexai.autoconfigure.embedding.VertexAiTextEmbeddingAutoConfiguration.class,
+ org.springframework.ai.model.vertexai.autoconfigure.gemini.VertexAiGeminiChatAutoConfiguration.class,
+})
public class Application {
public static void main(String[] args) {
diff --git a/spring-ai-4/src/main/java/com/baeldung/springai/vertexai/Application.java b/spring-ai-4/src/main/java/com/baeldung/springai/vertexai/Application.java
new file mode 100644
index 000000000000..4104be361c7f
--- /dev/null
+++ b/spring-ai-4/src/main/java/com/baeldung/springai/vertexai/Application.java
@@ -0,0 +1,22 @@
+package com.baeldung.springai.vertexai;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication(exclude = {
+ org.springframework.ai.model.openai.autoconfigure.OpenAiAudioSpeechAutoConfiguration.class,
+ org.springframework.ai.model.openai.autoconfigure.OpenAiAudioTranscriptionAutoConfiguration.class,
+ org.springframework.ai.model.openai.autoconfigure.OpenAiChatAutoConfiguration.class,
+ org.springframework.ai.model.openai.autoconfigure.OpenAiEmbeddingAutoConfiguration.class,
+ org.springframework.ai.model.openai.autoconfigure.OpenAiImageAutoConfiguration.class,
+ org.springframework.ai.model.openai.autoconfigure.OpenAiModerationAutoConfiguration.class
+})
+public class Application {
+
+ public static void main(String[] args) {
+ SpringApplication app = new SpringApplication(Application.class);
+ app.setAdditionalProfiles("vertexai");
+ app.run(args);
+ }
+
+}
diff --git a/spring-ai-4/src/main/java/com/baeldung/springai/vertexai/ChatController.java b/spring-ai-4/src/main/java/com/baeldung/springai/vertexai/ChatController.java
new file mode 100644
index 000000000000..c60018bd56b0
--- /dev/null
+++ b/spring-ai-4/src/main/java/com/baeldung/springai/vertexai/ChatController.java
@@ -0,0 +1,25 @@
+package com.baeldung.springai.vertexai;
+
+import javax.validation.constraints.NotNull;
+
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+public class ChatController {
+
+ private final ChatService chatService;
+
+ public ChatController(ChatService chatService) {
+ this.chatService = chatService;
+ }
+
+ @PostMapping("/chat")
+ public ResponseEntity chat(@RequestBody @NotNull String prompt) {
+ String response = chatService.chat(prompt);
+ return ResponseEntity.ok(response);
+ }
+
+}
diff --git a/spring-ai-4/src/main/java/com/baeldung/springai/vertexai/ChatService.java b/spring-ai-4/src/main/java/com/baeldung/springai/vertexai/ChatService.java
new file mode 100644
index 000000000000..db695023ca0a
--- /dev/null
+++ b/spring-ai-4/src/main/java/com/baeldung/springai/vertexai/ChatService.java
@@ -0,0 +1,29 @@
+package com.baeldung.springai.vertexai;
+
+import org.springframework.ai.chat.client.ChatClient;
+import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
+import org.springframework.ai.chat.memory.ChatMemory;
+import org.springframework.ai.chat.model.ChatModel;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.annotation.SessionScope;
+
+@Component
+@SessionScope
+public class ChatService {
+
+ private final ChatClient chatClient;
+
+ public ChatService(ChatModel chatModel, ChatMemory chatMemory) {
+ this.chatClient = ChatClient.builder(chatModel)
+ .defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())
+ .build();
+ }
+
+ public String chat(String prompt) {
+ return chatClient.prompt()
+ .user(userMessage -> userMessage.text(prompt))
+ .call()
+ .content();
+ }
+
+}
diff --git a/spring-ai-4/src/main/java/com/baeldung/springai/vertexai/MultiModalEmbeddingController.java b/spring-ai-4/src/main/java/com/baeldung/springai/vertexai/MultiModalEmbeddingController.java
new file mode 100644
index 000000000000..8cc834bc71d5
--- /dev/null
+++ b/spring-ai-4/src/main/java/com/baeldung/springai/vertexai/MultiModalEmbeddingController.java
@@ -0,0 +1,30 @@
+package com.baeldung.springai.vertexai;
+
+import javax.validation.constraints.NotNull;
+
+import org.springframework.ai.embedding.EmbeddingResponse;
+import org.springframework.http.ResponseEntity;
+import org.springframework.util.MimeType;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+
+@RestController
+public class MultiModalEmbeddingController {
+
+ private final MultiModalEmbeddingService embeddingService;
+
+ public MultiModalEmbeddingController(MultiModalEmbeddingService embeddingService) {
+ this.embeddingService = embeddingService;
+ }
+
+ @PostMapping("/embedding/image")
+ public ResponseEntity getEmbedding(@RequestParam("image") @NotNull MultipartFile imageFile) {
+ EmbeddingResponse response = embeddingService.getEmbedding(
+ MimeType.valueOf(imageFile.getContentType()),
+ imageFile.getResource());
+ return ResponseEntity.ok(response);
+ }
+
+}
\ No newline at end of file
diff --git a/spring-ai-4/src/main/java/com/baeldung/springai/vertexai/MultiModalEmbeddingService.java b/spring-ai-4/src/main/java/com/baeldung/springai/vertexai/MultiModalEmbeddingService.java
new file mode 100644
index 000000000000..d44ee5864787
--- /dev/null
+++ b/spring-ai-4/src/main/java/com/baeldung/springai/vertexai/MultiModalEmbeddingService.java
@@ -0,0 +1,30 @@
+package com.baeldung.springai.vertexai;
+
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.ai.content.Media;
+import org.springframework.ai.document.Document;
+import org.springframework.ai.embedding.DocumentEmbeddingModel;
+import org.springframework.ai.embedding.DocumentEmbeddingRequest;
+import org.springframework.ai.embedding.EmbeddingResponse;
+import org.springframework.core.io.Resource;
+import org.springframework.stereotype.Service;
+import org.springframework.util.MimeType;
+
+@Service
+public class MultiModalEmbeddingService {
+
+ private final DocumentEmbeddingModel documentEmbeddingModel;
+
+ public MultiModalEmbeddingService(DocumentEmbeddingModel documentEmbeddingModel) {
+ this.documentEmbeddingModel = documentEmbeddingModel;
+ }
+
+ public EmbeddingResponse getEmbedding(MimeType mimeType, Resource resource) {
+ Document document = new Document(new Media(mimeType, resource), Map.of());
+ DocumentEmbeddingRequest request = new DocumentEmbeddingRequest(List.of(document));
+ return documentEmbeddingModel.call(request);
+ }
+
+}
diff --git a/spring-ai-4/src/main/java/com/baeldung/springai/vertexai/TextEmbeddingController.java b/spring-ai-4/src/main/java/com/baeldung/springai/vertexai/TextEmbeddingController.java
new file mode 100644
index 000000000000..6e45290457dd
--- /dev/null
+++ b/spring-ai-4/src/main/java/com/baeldung/springai/vertexai/TextEmbeddingController.java
@@ -0,0 +1,26 @@
+package com.baeldung.springai.vertexai;
+
+import javax.validation.constraints.NotNull;
+
+import org.springframework.ai.embedding.EmbeddingResponse;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+public class TextEmbeddingController {
+
+ private final TextEmbeddingService textEmbeddingService;
+
+ public TextEmbeddingController(TextEmbeddingService textEmbeddingService) {
+ this.textEmbeddingService = textEmbeddingService;
+ }
+
+ @PostMapping("/embedding/text")
+ public ResponseEntity getEmbedding(@RequestBody @NotNull String text) {
+ EmbeddingResponse response = textEmbeddingService.getEmbedding(text);
+ return ResponseEntity.ok(response);
+ }
+
+}
\ No newline at end of file
diff --git a/spring-ai-4/src/main/java/com/baeldung/springai/vertexai/TextEmbeddingService.java b/spring-ai-4/src/main/java/com/baeldung/springai/vertexai/TextEmbeddingService.java
new file mode 100644
index 000000000000..3a351134b67e
--- /dev/null
+++ b/spring-ai-4/src/main/java/com/baeldung/springai/vertexai/TextEmbeddingService.java
@@ -0,0 +1,24 @@
+package com.baeldung.springai.vertexai;
+
+import java.util.Arrays;
+
+import org.springframework.ai.embedding.EmbeddingModel;
+import org.springframework.ai.embedding.EmbeddingRequest;
+import org.springframework.ai.embedding.EmbeddingResponse;
+import org.springframework.stereotype.Service;
+
+@Service
+public class TextEmbeddingService {
+
+ private final EmbeddingModel embeddingModel;
+
+ public TextEmbeddingService(EmbeddingModel embeddingModel) {
+ this.embeddingModel = embeddingModel;
+ }
+
+ public EmbeddingResponse getEmbedding(String... texts) {
+ EmbeddingRequest request = new EmbeddingRequest(Arrays.asList(texts), null);
+ return embeddingModel.call(request);
+ }
+
+}
diff --git a/spring-ai-4/src/main/resources/application-vertexai.yml b/spring-ai-4/src/main/resources/application-vertexai.yml
new file mode 100644
index 000000000000..ae2fd5253d1e
--- /dev/null
+++ b/spring-ai-4/src/main/resources/application-vertexai.yml
@@ -0,0 +1,14 @@
+spring:
+ ai:
+ vertex:
+ ai:
+ gemini:
+ project-id: "c1-lumion"
+ location: "europe-west1"
+ model: "gemini-2.0-flash-lite"
+ embedding:
+ project-id: "c1-lumion"
+ location: "europe-west1"
+ text:
+ options:
+ model: "gemini-embedding-001"
diff --git a/spring-ai-4/src/test/java/com/baeldung/springai/vertexai/ChatServiceLiveTest.java b/spring-ai-4/src/test/java/com/baeldung/springai/vertexai/ChatServiceLiveTest.java
new file mode 100644
index 000000000000..240efa3886f3
--- /dev/null
+++ b/spring-ai-4/src/test/java/com/baeldung/springai/vertexai/ChatServiceLiveTest.java
@@ -0,0 +1,25 @@
+package com.baeldung.springai.vertexai;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.ActiveProfiles;
+
+@SpringBootTest
+@ActiveProfiles("vertexai")
+class ChatServiceLiveTest {
+
+ private static final String PROMPT = "Tell me who you are?";
+
+ @Autowired
+ private ChatService chatService;
+
+ @Test
+ void whenChatServiceIsCalled_thenServiceReturnsNonEmptyResponse() {
+ String response = chatService.chat(PROMPT);
+ assertThat(response).isNotEmpty();
+ }
+
+}
\ No newline at end of file
diff --git a/spring-ai-4/src/test/java/com/baeldung/springai/vertexai/MultiModalEmbeddingServiceLiveTest.java b/spring-ai-4/src/test/java/com/baeldung/springai/vertexai/MultiModalEmbeddingServiceLiveTest.java
new file mode 100644
index 000000000000..17bacc44c5ef
--- /dev/null
+++ b/spring-ai-4/src/test/java/com/baeldung/springai/vertexai/MultiModalEmbeddingServiceLiveTest.java
@@ -0,0 +1,32 @@
+package com.baeldung.springai.vertexai;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.ai.embedding.EmbeddingResponse;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.Resource;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.util.MimeTypeUtils;
+
+@SpringBootTest
+@ActiveProfiles("vertexai")
+class MultiModalEmbeddingServiceLiveTest {
+
+ private static final String IMAGE_PATH = "image/chiikawa.png";
+
+ @Autowired
+ private MultiModalEmbeddingService embeddingService;
+
+ @Test
+ void whenGetEmbeddings_thenReturnEmbeddingResponse() {
+ Resource imageResource = new ClassPathResource(IMAGE_PATH);
+ EmbeddingResponse response = embeddingService.getEmbedding(MimeTypeUtils.IMAGE_PNG, imageResource);
+ assertThat(response).isNotNull();
+ assertThat(response.getResults()).isNotNull();
+ assertThat(response.getResults().isEmpty()).isFalse();
+ }
+
+}
\ No newline at end of file
diff --git a/spring-ai-4/src/test/java/com/baeldung/springai/vertexai/TextEmbeddingServiceLiveTest.java b/spring-ai-4/src/test/java/com/baeldung/springai/vertexai/TextEmbeddingServiceLiveTest.java
new file mode 100644
index 000000000000..2af34550adfd
--- /dev/null
+++ b/spring-ai-4/src/test/java/com/baeldung/springai/vertexai/TextEmbeddingServiceLiveTest.java
@@ -0,0 +1,27 @@
+package com.baeldung.springai.vertexai;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.ai.embedding.EmbeddingResponse;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.ActiveProfiles;
+
+@SpringBootTest
+@ActiveProfiles("vertexai")
+class TextEmbeddingServiceLiveTest {
+
+ @Autowired
+ private TextEmbeddingService embeddingService;
+
+ @Test
+ void whenGetEmbeddings_thenReturnEmbeddingResponse() {
+ String text = "This is a test string for embedding.";
+ EmbeddingResponse response = embeddingService.getEmbedding(text);
+ assertThat(response).isNotNull();
+ assertThat(response.getResults()).isNotNull();
+ assertThat(response.getResults().isEmpty()).isFalse();
+ }
+
+}
\ No newline at end of file