diff --git a/models/spring-ai-oci-genai/README.md b/models/spring-ai-oci-genai/README.md
new file mode 100644
index 0000000000..16d73d5185
--- /dev/null
+++ b/models/spring-ai-oci-genai/README.md
@@ -0,0 +1 @@
+[Oracle Cloud Infrastructure GenAI Documentation](https://docs.oracle.com/en-us/iaas/Content/generative-ai/overview.htm)
diff --git a/models/spring-ai-oci-genai/pom.xml b/models/spring-ai-oci-genai/pom.xml
new file mode 100644
index 0000000000..fc007e9f9b
--- /dev/null
+++ b/models/spring-ai-oci-genai/pom.xml
@@ -0,0 +1,69 @@
+
+
+ 4.0.0
+
+ org.springframework.ai
+ spring-ai
+ 1.0.0-SNAPSHOT
+ ../../pom.xml
+
+ spring-ai-oci-genai
+ jar
+ Spring AI Model - OCI GenAI
+ OCI GenAI models support
+ https://github.com/spring-projects/spring-ai
+
+
+ https://github.com/spring-projects/spring-ai
+ git://github.com/spring-projects/spring-ai.git
+ git@github.com:spring-projects/spring-ai.git
+
+
+
+
+
+
+ org.springframework.ai
+ spring-ai-core
+ ${project.parent.version}
+
+
+
+ com.oracle.oci.sdk
+ oci-java-sdk-shaded-full
+ ${oci-sdk-version}
+
+
+
+ com.oracle.oci.sdk
+ oci-java-sdk-addons-oke-workload-identity
+ ${oci-sdk-version}
+
+
+
+
+ org.springframework.boot
+ spring-boot
+
+
+
+ org.springframework
+ spring-context-support
+
+
+ org.springframework.boot
+ spring-boot-starter-logging
+
+
+
+
+ org.springframework.ai
+ spring-ai-test
+ ${project.version}
+ test
+
+
+
+
+
diff --git a/models/spring-ai-oci-genai/src/main/java/org/springframework/ai/oci/OCIEmbeddingModel.java b/models/spring-ai-oci-genai/src/main/java/org/springframework/ai/oci/OCIEmbeddingModel.java
new file mode 100644
index 0000000000..123e0705f3
--- /dev/null
+++ b/models/spring-ai-oci-genai/src/main/java/org/springframework/ai/oci/OCIEmbeddingModel.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2024 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.ai.oci;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import com.oracle.bmc.generativeaiinference.GenerativeAiInference;
+import com.oracle.bmc.generativeaiinference.model.DedicatedServingMode;
+import com.oracle.bmc.generativeaiinference.model.EmbedTextDetails;
+import com.oracle.bmc.generativeaiinference.model.EmbedTextResult;
+import com.oracle.bmc.generativeaiinference.model.OnDemandServingMode;
+import com.oracle.bmc.generativeaiinference.model.ServingMode;
+import com.oracle.bmc.generativeaiinference.requests.EmbedTextRequest;
+import io.micrometer.observation.ObservationRegistry;
+import org.springframework.ai.chat.metadata.EmptyUsage;
+import org.springframework.ai.document.Document;
+import org.springframework.ai.embedding.AbstractEmbeddingModel;
+import org.springframework.ai.embedding.Embedding;
+import org.springframework.ai.embedding.EmbeddingOptions;
+import org.springframework.ai.embedding.EmbeddingRequest;
+import org.springframework.ai.embedding.EmbeddingResponse;
+import org.springframework.ai.embedding.EmbeddingResponseMetadata;
+import org.springframework.ai.embedding.observation.DefaultEmbeddingModelObservationConvention;
+import org.springframework.ai.embedding.observation.EmbeddingModelObservationContext;
+import org.springframework.ai.embedding.observation.EmbeddingModelObservationConvention;
+import org.springframework.ai.embedding.observation.EmbeddingModelObservationDocumentation;
+import org.springframework.ai.model.ModelOptionsUtils;
+import org.springframework.ai.observation.conventions.AiProvider;
+import org.springframework.util.Assert;
+
+/**
+ * {@link org.springframework.ai.embedding.EmbeddingModel} implementation that uses the
+ * OCI GenAI Embedding API.
+ *
+ * @author Anders Swanson
+ * @since 1.0.0
+ */
+public class OCIEmbeddingModel extends AbstractEmbeddingModel {
+
+ // The OCI GenAI API has a batch size of 96 for embed text requests.
+ private static final int EMBEDTEXT_BATCH_SIZE = 96;
+
+ private static final EmbeddingModelObservationConvention DEFAULT_OBSERVATION_CONVENTION = new DefaultEmbeddingModelObservationConvention();
+
+ private final GenerativeAiInference genAi;
+
+ private final OCIEmbeddingOptions options;
+
+ private final ObservationRegistry observationRegistry;
+
+ private final EmbeddingModelObservationConvention observationConvention = DEFAULT_OBSERVATION_CONVENTION;
+
+ public OCIEmbeddingModel(GenerativeAiInference genAi, OCIEmbeddingOptions options) {
+ this(genAi, options, ObservationRegistry.NOOP);
+ }
+
+ public OCIEmbeddingModel(GenerativeAiInference genAi, OCIEmbeddingOptions options,
+ ObservationRegistry observationRegistry) {
+ Assert.notNull(genAi, "com.oracle.bmc.generativeaiinference.GenerativeAiInferenceClient must not be null");
+ Assert.notNull(options, "options must not be null");
+ Assert.notNull(observationRegistry, "observationRegistry must not be null");
+ this.genAi = genAi;
+ this.options = options;
+ this.observationRegistry = observationRegistry;
+ }
+
+ @Override
+ public EmbeddingResponse call(EmbeddingRequest request) {
+ Assert.notEmpty(request.getInstructions(), "At least one text is required!");
+ OCIEmbeddingOptions runtimeOptions = mergeOptions(request.getOptions(), options);
+ List embedTextRequests = createRequests(request.getInstructions(), runtimeOptions);
+
+ EmbeddingModelObservationContext context = EmbeddingModelObservationContext.builder()
+ .embeddingRequest(request)
+ .provider(AiProvider.OCI_GENAI.value())
+ .requestOptions(runtimeOptions)
+ .build();
+
+ return EmbeddingModelObservationDocumentation.EMBEDDING_MODEL_OPERATION
+ .observation(this.observationConvention, DEFAULT_OBSERVATION_CONVENTION, () -> context,
+ this.observationRegistry)
+ .observe(() -> embedAllWithContext(embedTextRequests, context));
+ }
+
+ @Override
+ public float[] embed(Document document) {
+ return embed(document.getContent());
+ }
+
+ private EmbeddingResponse embedAllWithContext(List embedTextRequests,
+ EmbeddingModelObservationContext context) {
+ String modelId = null;
+ AtomicInteger index = new AtomicInteger(0);
+ List embeddings = new ArrayList<>();
+ for (EmbedTextRequest embedTextRequest : embedTextRequests) {
+ EmbedTextResult embedTextResult = genAi.embedText(embedTextRequest).getEmbedTextResult();
+ if (modelId == null) {
+ modelId = embedTextResult.getModelId();
+ }
+ for (List e : embedTextResult.getEmbeddings()) {
+ float[] data = toFloats(e);
+ embeddings.add(new Embedding(data, index.getAndIncrement()));
+ }
+ }
+ EmbeddingResponseMetadata metadata = new EmbeddingResponseMetadata();
+ metadata.setModel(modelId);
+ metadata.setUsage(new EmptyUsage());
+ EmbeddingResponse embeddingResponse = new EmbeddingResponse(embeddings, metadata);
+ context.setResponse(embeddingResponse);
+ return embeddingResponse;
+ }
+
+ private ServingMode servingMode(OCIEmbeddingOptions embeddingOptions) {
+ return switch (embeddingOptions.getServingMode()) {
+ case "dedicated" -> DedicatedServingMode.builder().endpointId(embeddingOptions.getModel()).build();
+ case "on-demand" -> OnDemandServingMode.builder().modelId(embeddingOptions.getModel()).build();
+ default -> throw new IllegalArgumentException(
+ "unknown serving mode for OCI embedding model: " + embeddingOptions.getServingMode());
+ };
+ }
+
+ private List createRequests(List inputs, OCIEmbeddingOptions embeddingOptions) {
+ int size = inputs.size();
+ List requests = new ArrayList<>();
+ for (int i = 0; i < inputs.size(); i += EMBEDTEXT_BATCH_SIZE) {
+ List batch = inputs.subList(i, Math.min(i + EMBEDTEXT_BATCH_SIZE, size));
+ requests.add(createRequest(batch, embeddingOptions));
+ }
+ return requests;
+ }
+
+ private EmbedTextRequest createRequest(List inputs, OCIEmbeddingOptions embeddingOptions) {
+ EmbedTextDetails embedTextDetails = EmbedTextDetails.builder()
+ .servingMode(servingMode(embeddingOptions))
+ .compartmentId(embeddingOptions.getCompartment())
+ .inputs(inputs)
+ .truncate(Objects.requireNonNullElse(embeddingOptions.getTruncate(), EmbedTextDetails.Truncate.End))
+ .build();
+ return EmbedTextRequest.builder().embedTextDetails(embedTextDetails).build();
+ }
+
+ private OCIEmbeddingOptions mergeOptions(EmbeddingOptions embeddingOptions, OCIEmbeddingOptions defaultOptions) {
+ if (embeddingOptions instanceof OCIEmbeddingOptions) {
+ OCIEmbeddingOptions dynamicOptions = ModelOptionsUtils.merge(embeddingOptions, defaultOptions,
+ OCIEmbeddingOptions.class);
+ if (dynamicOptions != null) {
+ return dynamicOptions;
+ }
+ }
+ return defaultOptions;
+ }
+
+ private float[] toFloats(List embedding) {
+ float[] floats = new float[embedding.size()];
+ for (int i = 0; i < embedding.size(); i++) {
+ floats[i] = embedding.get(i);
+ }
+ return floats;
+ }
+
+}
diff --git a/models/spring-ai-oci-genai/src/main/java/org/springframework/ai/oci/OCIEmbeddingOptions.java b/models/spring-ai-oci-genai/src/main/java/org/springframework/ai/oci/OCIEmbeddingOptions.java
new file mode 100644
index 0000000000..e72f5359f5
--- /dev/null
+++ b/models/spring-ai-oci-genai/src/main/java/org/springframework/ai/oci/OCIEmbeddingOptions.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2024 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.ai.oci;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.oracle.bmc.generativeaiinference.model.EmbedTextDetails;
+import org.springframework.ai.embedding.EmbeddingOptions;
+
+/**
+ * The configuration information for OCI embedding requests
+ *
+ * @author Anders Swanson
+ */
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class OCIEmbeddingOptions implements EmbeddingOptions {
+
+ private @JsonProperty("model") String model;
+
+ private @JsonProperty("compartment") String compartment;
+
+ private @JsonProperty("servingMode") String servingMode;
+
+ private @JsonProperty("truncate") EmbedTextDetails.Truncate truncate;
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static class Builder {
+
+ private final OCIEmbeddingOptions options = new OCIEmbeddingOptions();
+
+ public Builder withModel(String model) {
+ this.options.setModel(model);
+ return this;
+ }
+
+ public Builder withCompartment(String compartment) {
+ this.options.setCompartment(compartment);
+ return this;
+ }
+
+ public Builder withServingMode(String servingMode) {
+ this.options.setServingMode(servingMode);
+ return this;
+ }
+
+ public Builder withTruncate(EmbedTextDetails.Truncate truncate) {
+ this.options.truncate = truncate;
+ return this;
+ }
+
+ public OCIEmbeddingOptions build() {
+ return this.options;
+ }
+
+ }
+
+ public String getModel() {
+ return this.model;
+ }
+
+ /**
+ * Not used by OCI GenAI.
+ * @return null
+ */
+ @Override
+ public Integer getDimensions() {
+ return null;
+ }
+
+ public void setModel(String model) {
+ this.model = model;
+ }
+
+ public String getCompartment() {
+ return compartment;
+ }
+
+ public void setCompartment(String compartment) {
+ this.compartment = compartment;
+ }
+
+ public String getServingMode() {
+ return servingMode;
+ }
+
+ public void setServingMode(String servingMode) {
+ this.servingMode = servingMode;
+ }
+
+ public EmbedTextDetails.Truncate getTruncate() {
+ return truncate;
+ }
+
+ public void setTruncate(EmbedTextDetails.Truncate truncate) {
+ this.truncate = truncate;
+ }
+
+}
diff --git a/models/spring-ai-oci-genai/src/test/java/org/springframework/ai/oci/BaseEmbeddingModelTest.java b/models/spring-ai-oci-genai/src/test/java/org/springframework/ai/oci/BaseEmbeddingModelTest.java
new file mode 100644
index 0000000000..5124bd734e
--- /dev/null
+++ b/models/spring-ai-oci-genai/src/test/java/org/springframework/ai/oci/BaseEmbeddingModelTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2024 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.ai.oci;
+
+import java.io.IOException;
+import java.nio.file.Paths;
+
+import com.oracle.bmc.Region;
+import com.oracle.bmc.auth.ConfigFileAuthenticationDetailsProvider;
+import com.oracle.bmc.generativeaiinference.GenerativeAiInferenceClient;
+
+public class BaseEmbeddingModelTest {
+
+ public static final String OCI_COMPARTMENT_ID_KEY = "OCI_COMPARTMENT_ID";
+
+ public static final String EMBEDDING_MODEL_V2 = "cohere.embed-english-light-v2.0";
+
+ public static final String EMBEDDING_MODEL_V3 = "cohere.embed-english-v3.0";
+
+ private static final String CONFIG_FILE = Paths.get(System.getProperty("user.home"), ".oci", "config").toString();
+
+ private static final String PROFILE = "DEFAULT";
+
+ private static final String REGION = "us-chicago-1";
+
+ private static final String COMPARTMENT_ID = System.getenv(OCI_COMPARTMENT_ID_KEY);
+
+ /**
+ * Create an OCIEmbeddingModel instance using a config file authentication provider.
+ * @return OCIEmbeddingModel instance
+ */
+ public static OCIEmbeddingModel get() {
+ try {
+ ConfigFileAuthenticationDetailsProvider authProvider = new ConfigFileAuthenticationDetailsProvider(
+ CONFIG_FILE, PROFILE);
+ GenerativeAiInferenceClient aiClient = GenerativeAiInferenceClient.builder()
+ .region(Region.valueOf(REGION))
+ .build(authProvider);
+ OCIEmbeddingOptions options = OCIEmbeddingOptions.builder()
+ .withModel(EMBEDDING_MODEL_V2)
+ .withCompartment(COMPARTMENT_ID)
+ .withServingMode("on-demand")
+ .build();
+ return new OCIEmbeddingModel(aiClient, options);
+ }
+ catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+}
diff --git a/models/spring-ai-oci-genai/src/test/java/org/springframework/ai/oci/OCIEmbeddingModelIT.java b/models/spring-ai-oci-genai/src/test/java/org/springframework/ai/oci/OCIEmbeddingModelIT.java
new file mode 100644
index 0000000000..8d25240bf8
--- /dev/null
+++ b/models/spring-ai-oci-genai/src/test/java/org/springframework/ai/oci/OCIEmbeddingModelIT.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2024 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.ai.oci;
+
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
+import org.springframework.ai.document.Document;
+import org.springframework.ai.embedding.EmbeddingRequest;
+import org.springframework.ai.embedding.EmbeddingResponse;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.springframework.ai.oci.BaseEmbeddingModelTest.OCI_COMPARTMENT_ID_KEY;
+
+@EnabledIfEnvironmentVariable(named = OCI_COMPARTMENT_ID_KEY, matches = ".+")
+public class OCIEmbeddingModelIT extends BaseEmbeddingModelTest {
+
+ private final OCIEmbeddingModel embeddingModel = get();
+
+ private final List content = List.of("How many states are in the USA?", "How many states are in India?");
+
+ @Test
+ void embed() {
+ float[] embedding = embeddingModel.embed(new Document("How many provinces are in Canada?"));
+ assertThat(embedding).hasSize(1024);
+ }
+
+ @Test
+ void call() {
+ EmbeddingResponse response = embeddingModel.call(new EmbeddingRequest(content, null));
+ assertThat(response).isNotNull();
+ assertThat(response.getResults()).hasSize(2);
+ assertThat(response.getMetadata().getModel()).isEqualTo(EMBEDDING_MODEL_V2);
+ }
+
+ @Test
+ void callWithOptions() {
+ EmbeddingResponse response = embeddingModel
+ .call(new EmbeddingRequest(content, OCIEmbeddingOptions.builder().withModel(EMBEDDING_MODEL_V3).build()));
+ assertThat(response).isNotNull();
+ assertThat(response.getResults()).hasSize(2);
+ assertThat(response.getMetadata().getModel()).isEqualTo(EMBEDDING_MODEL_V3);
+ }
+
+}
diff --git a/pom.xml b/pom.xml
index 3aa8ccff5d..cdbfe43f26 100644
--- a/pom.xml
+++ b/pom.xml
@@ -67,6 +67,7 @@
models/spring-ai-huggingface
models/spring-ai-minimax
models/spring-ai-mistral-ai
+ models/spring-ai-oci-genai
models/spring-ai-ollama
models/spring-ai-openai
models/spring-ai-postgresml
@@ -86,6 +87,7 @@
spring-ai-spring-boot-starters/spring-ai-starter-huggingface
spring-ai-spring-boot-starters/spring-ai-starter-minimax
spring-ai-spring-boot-starters/spring-ai-starter-mistral-ai
+ spring-ai-spring-boot-starters/spring-ai-starter-oci-genai
spring-ai-spring-boot-starters/spring-ai-starter-ollama
spring-ai-spring-boot-starters/spring-ai-starter-openai
spring-ai-spring-boot-starters/spring-ai-starter-postgresml-embedding
@@ -157,8 +159,10 @@
2.26.7
2.16.1
+
0.30.0
1.19.2
+ 3.46.1
26.41.0
1.9.1
2.0.5
diff --git a/spring-ai-bom/pom.xml b/spring-ai-bom/pom.xml
index 805d8fbbb7..838fabcce3 100644
--- a/spring-ai-bom/pom.xml
+++ b/spring-ai-bom/pom.xml
@@ -76,6 +76,12 @@
${project.version}
+
+ org.springframework.ai
+ spring-ai-oci-genai
+ ${project.version}
+
+
org.springframework.ai
spring-ai-ollama
@@ -313,6 +319,12 @@
${project.version}
+
+ org.springframework.ai
+ spring-ai-oci-genai-spring-boot-starter
+ ${project.version}
+
+
org.springframework.ai
spring-ai-ollama-spring-boot-starter
diff --git a/spring-ai-core/src/main/java/org/springframework/ai/observation/conventions/AiProvider.java b/spring-ai-core/src/main/java/org/springframework/ai/observation/conventions/AiProvider.java
index e3da38c628..cb9a76aea1 100644
--- a/spring-ai-core/src/main/java/org/springframework/ai/observation/conventions/AiProvider.java
+++ b/spring-ai-core/src/main/java/org/springframework/ai/observation/conventions/AiProvider.java
@@ -34,7 +34,8 @@ public enum AiProvider {
OLLAMA("ollama"),
OPENAI("openai"),
SPRING_AI("spring_ai"),
- VERTEX_AI("vertex_ai");
+ VERTEX_AI("vertex_ai"),
+ OCI_GENAI("oci_genai");
private final String value;
diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/nav.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/nav.adoc
index 7c544adf65..873cce6f44 100644
--- a/spring-ai-docs/src/main/antora/modules/ROOT/nav.adoc
+++ b/spring-ai-docs/src/main/antora/modules/ROOT/nav.adoc
@@ -37,13 +37,13 @@
// **** xref:api/chat/functions/zhipuai-chat-functions.adoc[Function Calling]
*** xref:api/chat/watsonx-ai-chat.adoc[watsonx.AI]
** xref:api/embeddings.adoc[]
-*** xref:api/embeddings/azure-openai-embeddings.adoc[Azure OpenAI]
*** xref:api/bedrock.adoc[Amazon Bedrock]
**** xref:api/embeddings/bedrock-cohere-embedding.adoc[Cohere]
**** xref:api/embeddings/bedrock-titan-embedding.adoc[Titan]
*** xref:api/embeddings/azure-openai-embeddings.adoc[Azure OpenAI]
*** xref:api/embeddings/mistralai-embeddings.adoc[Mistral AI]
*** xref:api/embeddings/minimax-embeddings.adoc[MiniMax]
+*** xref:api/embeddings/oci-genai-embeddings.adoc[OCI GenAI]
*** xref:api/embeddings/ollama-embeddings.adoc[Ollama]
*** xref:api/embeddings/onnx.adoc[(ONNX) Transformers]
*** xref:api/embeddings/openai-embeddings.adoc[OpenAI]
diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/embeddings.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/embeddings.adoc
index f796b9a6f9..f99c5442c6 100644
--- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/embeddings.adoc
+++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/embeddings.adoc
@@ -168,3 +168,4 @@ Internally the various `EmbeddingModel` implementations use different low-level
* xref:api/embeddings/vertexai-embeddings-text.adoc[Spring AI VertexAI Embeddings]
* xref:api/embeddings/vertexai-embeddings-palm2.adoc[Spring AI VertexAI PaLM2 Embeddings]
* xref:api/embeddings/mistralai-embeddings.adoc[Spring AI Mistral AI Embeddings]
+* xref:api/embeddings/oci-genai-embeddings.adoc[Spring AI Oracle Cloud Infrastructure GenAI Embeddings]
diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/embeddings/oci-genai-embeddings.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/embeddings/oci-genai-embeddings.adoc
new file mode 100644
index 0000000000..2912d39350
--- /dev/null
+++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/embeddings/oci-genai-embeddings.adoc
@@ -0,0 +1,173 @@
+= Oracle Cloud Infrastructure (OCI) GenAI Embeddings
+
+https://www.oracle.com/artificial-intelligence/generative-ai/generative-ai-service/[OCI GenAI Service] offers text embedding with on-demand models, or dedicated AI clusters.
+
+The https://docs.oracle.com/en-us/iaas/Content/generative-ai/embed-models.htm[OCI Embedding Models Page] and https://docs.oracle.com/en-us/iaas/Content/generative-ai/use-playground-embed.htm[OCI Text Embeddings Page] provide detailed information about using and hosting embedding models on OCI.
+
+== Prerequisites
+
+=== Add Repositories and BOM
+
+Spring AI artifacts are published in Spring Milestone and Snapshot repositories. Refer to the xref:getting-started.adoc#repositories[Repositories] section to add these repositories to your build system.
+
+To help with dependency management, Spring AI provides a BOM (bill of materials) to ensure that a consistent version of Spring AI is used throughout the entire project. Refer to the xref:getting-started.adoc#dependency-management[Dependency Management] section to add the Spring AI BOM to your build system.
+
+== Auto-configuration
+
+Spring AI provides Spring Boot auto-configuration for the OCI GenAI Embedding Client.
+To enable it add the following dependency to your project's Maven `pom.xml` file:
+
+[source, xml]
+----
+
+ org.springframework.ai
+ spring-ai-oci-genai-spring-boot-starter
+
+----
+
+or to your Gradle `build.gradle` build file.
+
+[source,groovy]
+----
+dependencies {
+ implementation 'org.springframework.ai:spring-ai-oci-genai-spring-boot-starter'
+}
+----
+
+TIP: Refer to the xref:getting-started.adoc#dependency-management[Dependency Management] section to add the Spring AI BOM to your build file.
+
+=== Embedding Properties
+
+The prefix `spring.ai.oci.genai` is the property prefix to configure the connection to OCI GenAI.
+
+[cols="3,5,1"]
+|====
+| Property | Description | Default
+
+| spring.ai.oci.genai.authenticationType | The type of authentication to use when authenticating to OCI. May be `file`, `instance-principal`, `workload-identity`, or `simple`. | file
+| spring.ai.oci.genai.region | OCI service region. | us-chicago-1
+| spring.ai.oci.genai.tenantId | OCI tenant OCID, used when authenticating with `simple` auth. | -
+| spring.ai.oci.genai.userId | OCI user OCID, used when authenticating with `simple` auth. | -
+| spring.ai.oci.genai.fingerprint | Private key fingerprint, used when authenticating with `simple` auth. | -
+| spring.ai.oci.genai.privateKey | Private key content, used when authenticating with `simple` auth. | -
+| spring.ai.oci.genai.passPhrase | Optional private key passphrase, used when authenticating with `simple` auth and a passphrase protected private key. | -
+| spring.ai.oci.genai.file | Path to OCI config file. Used when authenticating with `file` auth. | /.oci/config
+| spring.ai.oci.genai.profile | OCI profile name. Used when authenticating with `file` auth. | DEFAULT
+| spring.ai.oci.genai.endpoint | Optional OCI GenAI endpoint. | -
+
+|====
+
+
+The prefix `spring.ai.oci.genai.embedding` is the property prefix that configures the `EmbeddingModel` implementation for OCI GenAI
+
+[cols="3,5,1"]
+|====
+| Property | Description | Default
+
+| spring.ai.oci.genai.embedding.enabled | Enable OCI GenAI embedding model. | true
+| spring.ai.oci.genai.embedding.compartment | Model compartment OCID. | -
+| spring.ai.oci.genai.embedding.servingMode | The model serving mode to be used. May be `on-demand`, or `dedicated`. | on-demand
+| spring.ai.oci.genai.embedding.truncate | How to truncate text if it overruns the embedding context. May be `START`, or `END`. | END
+| spring.ai.oci.genai.embedding.model | The model or model endpoint used for embeddings. | -
+|====
+
+TIP: All properties prefixed with `spring.ai.oci.genai.embedding.options` can be overridden at runtime by adding a request specific <> to the `EmbeddingRequest` call.
+
+== Runtime Options [[embedding-options]]
+
+The `OCIEmbeddingOptions` provides the configuration information for the embedding requests.
+The `OCIEmbeddingOptions` offers a builder to create the options.
+
+At start time use the `OCIEmbeddingOptions` constructor to set the default options used for all embedding requests.
+At run-time you can override the default options, by passing a `OCIEmbeddingOptions` instance with your to the `EmbeddingRequest` request.
+
+For example to override the default model name for a specific request:
+
+[source,java]
+----
+EmbeddingResponse embeddingResponse = embeddingModel.call(
+ new EmbeddingRequest(List.of("Hello World", "World is big and salvation is near"),
+ OCIEmbeddingOptions.builder()
+ .withModel("my-other-embedding-model")
+ .build()
+));
+----
+
+
+== Sample Code
+
+This will create a `EmbeddingModel` implementation that you can inject into your class.
+Here is an example of a simple `@Controller` class that uses the `EmbeddingModel` implementation.
+
+[source,application.properties]
+----
+spring.ai.oci.genai.embedding.model=
+spring.ai.oci.genai.embedding.compartment=
+----
+
+[source,java]
+----
+@RestController
+public class EmbeddingController {
+
+ private final EmbeddingModel embeddingModel;
+
+ @Autowired
+ public EmbeddingController(EmbeddingModel embeddingModel) {
+ this.embeddingModel = embeddingModel;
+ }
+
+ @GetMapping("/ai/embedding")
+ public Map embed(@RequestParam(value = "message", defaultValue = "Tell me a joke") String message) {
+ EmbeddingResponse embeddingResponse = this.embeddingModel.embedForResponse(List.of(message));
+ return Map.of("embedding", embeddingResponse);
+ }
+}
+----
+
+== Manual Configuration
+
+If you prefer not to use the Spring Boot auto-configuration, you can manually configure the `OCIEmbeddingModel` in your application.
+For this add the `spring-oci-genai-openai` dependency to your project's Maven `pom.xml` file:
+[source, xml]
+----
+
+ org.springframework.ai
+ spring-oci-genai-openai
+
+----
+
+or to your Gradle `build.gradle` build file.
+
+[source,gradle]
+----
+dependencies {
+ implementation 'org.springframework.ai:spring-oci-genai-openai'
+}
+----
+
+TIP: Refer to the xref:getting-started.adoc#dependency-management[Dependency Management] section to add the Spring AI BOM to your build file.
+
+Next, create an `OCIEmbeddingModel` instance and use it to compute the similarity between two input texts:
+
+[source,java]
+----
+final String EMBEDDING_MODEL = "cohere.embed-english-light-v2.0";
+final String CONFIG_FILE = Paths.get(System.getProperty("user.home"), ".oci", "config").toString();
+final String PROFILE = "DEFAULT";
+final String REGION = "us-chicago-1";
+final String COMPARTMENT_ID = System.getenv("OCI_COMPARTMENT_ID");
+
+var authProvider = new ConfigFileAuthenticationDetailsProvider(
+ CONFIG_FILE, PROFILE);
+var aiClient = GenerativeAiInferenceClient.builder()
+ .region(Region.valueOf(REGION))
+ .build(authProvider);
+var options = OCIEmbeddingOptions.builder()
+ .withModel(EMBEDDING_MODEL)
+ .withCompartment(COMPARTMENT_ID)
+ .withServingMode("on-demand")
+ .build();
+var embeddingModel = new OCIEmbeddingModel(aiClient, options);
+List embedding = embeddingModel.embed(new Document("How many provinces are in Canada?"));
+----
diff --git a/spring-ai-spring-boot-autoconfigure/pom.xml b/spring-ai-spring-boot-autoconfigure/pom.xml
index 250d6246ca..9a0428fea0 100644
--- a/spring-ai-spring-boot-autoconfigure/pom.xml
+++ b/spring-ai-spring-boot-autoconfigure/pom.xml
@@ -63,6 +63,13 @@
true
+
+ org.springframework.ai
+ spring-ai-oci-genai
+ ${project.parent.version}
+ true
+
+
org.springframework.ai
spring-ai-huggingface
@@ -425,6 +432,13 @@
test
+
+ org.testcontainers
+ oracle-free
+ 1.19.8
+ test
+
+
org.testcontainers
junit-jupiter
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/oci/genai/OCIConnectionProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/oci/genai/OCIConnectionProperties.java
new file mode 100644
index 0000000000..6de993705d
--- /dev/null
+++ b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/oci/genai/OCIConnectionProperties.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2024 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.ai.autoconfigure.oci.genai;
+
+import java.nio.file.Paths;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.util.StringUtils;
+
+/**
+ * @author Anders Swanson
+ */
+@ConfigurationProperties(OCIConnectionProperties.CONFIG_PREFIX)
+public class OCIConnectionProperties {
+
+ private static final String DEFAULT_PROFILE = "DEFAULT";
+
+ public static final String CONFIG_PREFIX = "spring.ai.oci.genai";
+
+ public enum AuthenticationType {
+
+ FILE("file"), INSTANCE_PRINCIPAL("instance-principal"), WORKLOAD_IDENTITY("workload-identity"),
+ SIMPLE("simple");
+
+ private final String authType;
+
+ AuthenticationType(String authType) {
+ this.authType = authType;
+ }
+
+ public String getAuthType() {
+ return this.authType;
+ }
+
+ }
+
+ private AuthenticationType authenticationType = AuthenticationType.FILE;
+
+ private String profile;
+
+ private String file = Paths.get(System.getProperty("user.home"), ".oci", "config").toString();
+
+ private String tenantId;
+
+ private String userId;
+
+ private String fingerprint;
+
+ private String privateKey;
+
+ private String passPhrase;
+
+ private String region = "us-chicago-1";
+
+ private String endpoint;
+
+ public String getRegion() {
+ return region;
+ }
+
+ public void setRegion(String region) {
+ this.region = region;
+ }
+
+ public String getPassPhrase() {
+ return passPhrase;
+ }
+
+ public void setPassPhrase(String passPhrase) {
+ this.passPhrase = passPhrase;
+ }
+
+ public String getPrivateKey() {
+ return privateKey;
+ }
+
+ public void setPrivateKey(String privateKey) {
+ this.privateKey = privateKey;
+ }
+
+ public String getFingerprint() {
+ return fingerprint;
+ }
+
+ public void setFingerprint(String fingerprint) {
+ this.fingerprint = fingerprint;
+ }
+
+ public String getUserId() {
+ return userId;
+ }
+
+ public void setUserId(String userId) {
+ this.userId = userId;
+ }
+
+ public String getTenantId() {
+ return tenantId;
+ }
+
+ public void setTenantId(String tenantId) {
+ this.tenantId = tenantId;
+ }
+
+ public String getFile() {
+ return file;
+ }
+
+ public void setFile(String file) {
+ this.file = file;
+ }
+
+ public String getProfile() {
+ return StringUtils.hasText(profile) ? profile : DEFAULT_PROFILE;
+ }
+
+ public void setProfile(String profile) {
+ this.profile = profile;
+ }
+
+ public AuthenticationType getAuthenticationType() {
+ return authenticationType;
+ }
+
+ public void setAuthenticationType(AuthenticationType authenticationType) {
+ this.authenticationType = authenticationType;
+ }
+
+ public String getEndpoint() {
+ return endpoint;
+ }
+
+ public void setEndpoint(String endpoint) {
+ this.endpoint = endpoint;
+ }
+
+}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/oci/genai/OCIEmbeddingModelProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/oci/genai/OCIEmbeddingModelProperties.java
new file mode 100644
index 0000000000..cc2ef2b5f1
--- /dev/null
+++ b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/oci/genai/OCIEmbeddingModelProperties.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2024 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.ai.autoconfigure.oci.genai;
+
+import com.oracle.bmc.generativeaiinference.model.EmbedTextDetails;
+import org.springframework.ai.oci.OCIEmbeddingOptions;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+/**
+ * @author Anders Swanson
+ */
+@ConfigurationProperties(OCIEmbeddingModelProperties.CONFIG_PREFIX)
+public class OCIEmbeddingModelProperties {
+
+ public static final String CONFIG_PREFIX = "spring.ai.oci.genai.embedding";
+
+ private ServingMode servingMode = ServingMode.ON_DEMAND;
+
+ private EmbedTextDetails.Truncate truncate = EmbedTextDetails.Truncate.End;
+
+ private String compartment;
+
+ private String model;
+
+ private boolean enabled;
+
+ public OCIEmbeddingOptions getEmbeddingOptions() {
+ return OCIEmbeddingOptions.builder()
+ .withCompartment(compartment)
+ .withModel(model)
+ .withServingMode(servingMode.getMode())
+ .withTruncate(truncate)
+ .build();
+ }
+
+ public ServingMode getServingMode() {
+ return servingMode;
+ }
+
+ public void setServingMode(ServingMode servingMode) {
+ this.servingMode = servingMode;
+ }
+
+ public String getCompartment() {
+ return compartment;
+ }
+
+ public void setCompartment(String compartment) {
+ this.compartment = compartment;
+ }
+
+ public String getModel() {
+ return model;
+ }
+
+ public void setModel(String model) {
+ this.model = model;
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public EmbedTextDetails.Truncate getTruncate() {
+ return truncate;
+ }
+
+ public void setTruncate(EmbedTextDetails.Truncate truncate) {
+ this.truncate = truncate;
+ }
+
+}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/oci/genai/OCIGenAiAutoConfiguration.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/oci/genai/OCIGenAiAutoConfiguration.java
new file mode 100644
index 0000000000..9e8a64059f
--- /dev/null
+++ b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/oci/genai/OCIGenAiAutoConfiguration.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2024 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.ai.autoconfigure.oci.genai;
+
+import java.io.IOException;
+
+import com.oracle.bmc.ClientConfiguration;
+import com.oracle.bmc.Region;
+import com.oracle.bmc.auth.BasicAuthenticationDetailsProvider;
+import com.oracle.bmc.auth.ConfigFileAuthenticationDetailsProvider;
+import com.oracle.bmc.auth.InstancePrincipalsAuthenticationDetailsProvider;
+import com.oracle.bmc.auth.SimpleAuthenticationDetailsProvider;
+import com.oracle.bmc.auth.SimplePrivateKeySupplier;
+import com.oracle.bmc.auth.okeworkloadidentity.OkeWorkloadIdentityAuthenticationDetailsProvider;
+import com.oracle.bmc.generativeaiinference.GenerativeAiInferenceClient;
+import com.oracle.bmc.retrier.RetryConfiguration;
+import org.springframework.ai.oci.OCIEmbeddingModel;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.util.StringUtils;
+
+/**
+ * @author Anders Swanson
+ */
+@AutoConfiguration
+@ConditionalOnClass({ GenerativeAiInferenceClient.class, OCIEmbeddingModel.class })
+@EnableConfigurationProperties({ OCIConnectionProperties.class, OCIEmbeddingModelProperties.class })
+public class OCIGenAiAutoConfiguration {
+
+ @ConditionalOnMissingBean
+ @Bean
+ public GenerativeAiInferenceClient generativeAiInferenceClient(OCIConnectionProperties properties)
+ throws IOException {
+ ClientConfiguration clientConfiguration = ClientConfiguration.builder()
+ .retryConfiguration(RetryConfiguration.SDK_DEFAULT_RETRY_CONFIGURATION)
+ .build();
+ GenerativeAiInferenceClient.Builder builder = GenerativeAiInferenceClient.builder()
+ .configuration(clientConfiguration);
+ if (StringUtils.hasText(properties.getRegion())) {
+ builder.region(Region.valueOf(properties.getRegion()));
+ }
+ if (StringUtils.hasText(properties.getEndpoint())) {
+ builder.endpoint(properties.getEndpoint());
+ }
+ return builder.build(authenticationProvider(properties));
+ }
+
+ @Bean
+ @ConditionalOnProperty(prefix = OCIEmbeddingModelProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
+ matchIfMissing = true)
+ public OCIEmbeddingModel ociEmbeddingModel(GenerativeAiInferenceClient generativeAiClient,
+ OCIEmbeddingModelProperties properties) {
+ return new OCIEmbeddingModel(generativeAiClient, properties.getEmbeddingOptions());
+ }
+
+ private static BasicAuthenticationDetailsProvider authenticationProvider(OCIConnectionProperties properties)
+ throws IOException {
+ return switch (properties.getAuthenticationType()) {
+ case FILE -> new ConfigFileAuthenticationDetailsProvider(properties.getFile(), properties.getProfile());
+ case INSTANCE_PRINCIPAL -> InstancePrincipalsAuthenticationDetailsProvider.builder().build();
+ case WORKLOAD_IDENTITY -> OkeWorkloadIdentityAuthenticationDetailsProvider.builder().build();
+ case SIMPLE -> SimpleAuthenticationDetailsProvider.builder()
+ .userId(properties.getUserId())
+ .tenantId(properties.getTenantId())
+ .fingerprint(properties.getFingerprint())
+ .privateKeySupplier(new SimplePrivateKeySupplier(properties.getPrivateKey()))
+ .passPhrase(properties.getPassPhrase())
+ .region(Region.valueOf(properties.getRegion()))
+ .build();
+ };
+ }
+
+}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/oci/genai/ServingMode.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/oci/genai/ServingMode.java
new file mode 100644
index 0000000000..7cb2299a2c
--- /dev/null
+++ b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/oci/genai/ServingMode.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2024 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.ai.autoconfigure.oci.genai;
+
+/**
+ * @author Anders Swanson
+ */
+public enum ServingMode {
+
+ ON_DEMAND("on-demand"), DEDICATED("dedicated");
+
+ private final String mode;
+
+ ServingMode(String mode) {
+ this.mode = mode;
+ }
+
+ public String getMode() {
+ return mode;
+ }
+
+}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-ai-spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
index 9fe46cc260..d5b849aa3c 100644
--- a/spring-ai-spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
+++ b/spring-ai-spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -1,5 +1,6 @@
org.springframework.ai.autoconfigure.openai.OpenAiAutoConfiguration
org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiAutoConfiguration
+org.springframework.ai.autoconfigure.oci.genai.OCIGenAiAutoConfiguration
org.springframework.ai.autoconfigure.stabilityai.StabilityAiImageAutoConfiguration
org.springframework.ai.autoconfigure.transformers.TransformersEmbeddingModelAutoConfiguration
org.springframework.ai.autoconfigure.huggingface.HuggingfaceChatAutoConfiguration
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/oci/genai/OCIGenAiAutoConfigurationIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/oci/genai/OCIGenAiAutoConfigurationIT.java
new file mode 100644
index 0000000000..d67a8e0e71
--- /dev/null
+++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/oci/genai/OCIGenAiAutoConfigurationIT.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2024 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.ai.autoconfigure.oci.genai;
+
+import java.nio.file.Paths;
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
+import org.springframework.ai.embedding.EmbeddingRequest;
+import org.springframework.ai.embedding.EmbeddingResponse;
+import org.springframework.ai.oci.OCIEmbeddingModel;
+import org.springframework.boot.autoconfigure.AutoConfigurations;
+import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@EnabledIfEnvironmentVariable(named = OCIGenAiAutoConfigurationIT.COMPARTMENT_ID_KEY, matches = ".+")
+public class OCIGenAiAutoConfigurationIT {
+
+ public static final String COMPARTMENT_ID_KEY = "OCI_COMPARTMENT_ID";
+
+ private final String CONFIG_FILE = Paths.get(System.getProperty("user.home"), ".oci", "config").toString();
+
+ private final String COMPARTMENT_ID = System.getenv(COMPARTMENT_ID_KEY);
+
+ private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().withPropertyValues(
+ // @formatter:off
+ "spring.ai.oci.genai.authenticationType=file",
+ "spring.ai.oci.genai.file=" + CONFIG_FILE,
+ "spring.ai.oci.genai.embedding.compartment=" + COMPARTMENT_ID,
+ "spring.ai.oci.genai.embedding.servingMode=on-demand",
+ "spring.ai.oci.genai.embedding.model=cohere.embed-english-light-v2.0"
+ // @formatter:on
+ ).withConfiguration(AutoConfigurations.of(OCIGenAiAutoConfiguration.class));
+
+ @Test
+ void embeddings() {
+ contextRunner.run(context -> {
+ OCIEmbeddingModel embeddingModel = context.getBean(OCIEmbeddingModel.class);
+ assertThat(embeddingModel).isNotNull();
+ EmbeddingResponse response = embeddingModel
+ .call(new EmbeddingRequest(List.of("There are 50 states in the USA", "Canada has 10 provinces"), null));
+ assertThat(response).isNotNull();
+ assertThat(response.getResults()).hasSize(2);
+ });
+ }
+
+}
diff --git a/spring-ai-spring-boot-starters/spring-ai-starter-oci-genai/pom.xml b/spring-ai-spring-boot-starters/spring-ai-starter-oci-genai/pom.xml
new file mode 100644
index 0000000000..2fd347de32
--- /dev/null
+++ b/spring-ai-spring-boot-starters/spring-ai-starter-oci-genai/pom.xml
@@ -0,0 +1,40 @@
+
+
+ 4.0.0
+
+ org.springframework.ai
+ spring-ai
+ 1.0.0-SNAPSHOT
+ ../../pom.xml
+
+ spring-ai-oci-genai-spring-boot-starter
+ jar
+ Spring AI Starter - OCI GenAI
+ Spring AI OCI GenAI Auto Configuration
+ https://github.com/spring-projects/spring-ai
+
+
+ https://github.com/spring-projects/spring-ai
+ git://github.com/spring-projects/spring-ai.git
+ git@github.com:spring-projects/spring-ai.git
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+
+ org.springframework.ai
+ spring-ai-spring-boot-autoconfigure
+ ${project.parent.version}
+
+
+
+ org.springframework.ai
+ spring-ai-oci-genai
+ ${project.parent.version}
+
+
+