From 20d72c3662f237d1cbc963ec0c9dba7ad483eb29 Mon Sep 17 00:00:00 2001 From: Roshin Rajan Panackal Date: Thu, 28 Aug 2025 10:41:32 +0200 Subject: [PATCH 1/9] Request convenience --- .../orchestration/OrchestrationClient.java | 2 +- .../OrchestrationEmbeddingModel.java | 57 ++++++++++++ .../OrchestrationEmbeddingRequest.java | 88 +++++++++++++++++++ .../OrchestrationEmbeddingTest.java | 77 ++++++++++++++++ 4 files changed, 223 insertions(+), 1 deletion(-) create mode 100644 orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingModel.java create mode 100644 orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingRequest.java create mode 100644 orchestration/src/test/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingTest.java diff --git a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationClient.java b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationClient.java index d7e165cdb..988f39649 100644 --- a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationClient.java +++ b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationClient.java @@ -226,7 +226,7 @@ public Stream streamChatCompletionDeltas( * @since 1.9.0 */ @Nonnull - EmbeddingsPostResponse embed(@Nonnull final EmbeddingsPostRequest request) + public EmbeddingsPostResponse embed(@Nonnull final EmbeddingsPostRequest request) throws OrchestrationClientException { return executor.execute("/v2/embeddings", request, EmbeddingsPostResponse.class); } diff --git a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingModel.java b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingModel.java new file mode 100644 index 000000000..f146aebdd --- /dev/null +++ b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingModel.java @@ -0,0 +1,57 @@ +package com.sap.ai.sdk.orchestration; + +import com.google.common.annotations.Beta; +import com.sap.ai.sdk.core.AiModel; +import com.sap.ai.sdk.orchestration.model.EmbeddingsModelDetails; +import com.sap.ai.sdk.orchestration.model.EmbeddingsModelParams; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Value; +import lombok.With; +import lombok.experimental.Accessors; + +// ideally this is a record but exposes all args constructor which we want to avoid. Is it worth a +// value class? +// Currently, model list follow SAP Notes as the source of truth. But deprecated models are not +// listed there. +// Can we reuse existing enum from generated class? +@Beta +@With +@Value +@Accessors(fluent = true) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class OrchestrationEmbeddingModel implements AiModel { + @Nonnull String name; + @Nullable String version; + @Nullable Integer dimensions; + @Nullable Boolean normalize; + @Nullable EmbeddingsModelParams.EncodingFormatEnum encodingFormat; + + public OrchestrationEmbeddingModel(@Nonnull final String name) { + this(name, null, null, null, null); + } + + public static final OrchestrationEmbeddingModel TEXT_EMBEDDING_3_SMALL = + new OrchestrationEmbeddingModel("text-embedding-3-small"); + + public static final OrchestrationEmbeddingModel TEXT_EMBEDDING_3_LARGE = + new OrchestrationEmbeddingModel("text-embedding-3-large"); + + public static final OrchestrationEmbeddingModel AMAZON_TITAN_EMBED_TEXT = + new OrchestrationEmbeddingModel("amazon.titan-embed-text"); + + public static final OrchestrationEmbeddingModel NVIDIA_LLAMA_32_NV_EMBEDQA_1B = + new OrchestrationEmbeddingModel("nvidia--llama-3.2-nv-embedqa-1b"); + + EmbeddingsModelDetails createEmbeddingsModelDetails() { + + final var params = + EmbeddingsModelParams.create() + .dimensions(dimensions) + .normalize(normalize) + .encodingFormat(encodingFormat); + return EmbeddingsModelDetails.create().name(name).version(version).params(params); + } +} diff --git a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingRequest.java b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingRequest.java new file mode 100644 index 000000000..908107726 --- /dev/null +++ b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingRequest.java @@ -0,0 +1,88 @@ +package com.sap.ai.sdk.orchestration; + +import static com.sap.ai.sdk.orchestration.model.EmbeddingsInput.TypeEnum.DOCUMENT; +import static com.sap.ai.sdk.orchestration.model.EmbeddingsInput.TypeEnum.QUERY; +import static com.sap.ai.sdk.orchestration.model.EmbeddingsInput.TypeEnum.TEXT; +import static lombok.AccessLevel.PRIVATE; + +import com.google.common.annotations.Beta; +import com.google.common.collect.Lists; +import com.sap.ai.sdk.orchestration.model.EmbeddingsInput; +import com.sap.ai.sdk.orchestration.model.EmbeddingsInputText; +import com.sap.ai.sdk.orchestration.model.EmbeddingsModelConfig; +import com.sap.ai.sdk.orchestration.model.EmbeddingsModuleConfigs; +import com.sap.ai.sdk.orchestration.model.EmbeddingsOrchestrationConfig; +import com.sap.ai.sdk.orchestration.model.EmbeddingsPostRequest; +import com.sap.ai.sdk.orchestration.model.MaskingModuleConfigProviders; +import java.util.List; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import lombok.AllArgsConstructor; +import lombok.Value; +import lombok.With; +import lombok.experimental.Tolerate; + +// Do we need staged input builder here? +// Do we need an enum for tokenType? +@Beta +@Value +@AllArgsConstructor(access = PRIVATE) +public class OrchestrationEmbeddingRequest { + + @Nonnull OrchestrationEmbeddingModel model; + @Nonnull List tokens; + + @With(value = PRIVATE) + @Nullable + List masking; + + @With(value = PRIVATE) + @Nullable + EmbeddingsInput.TypeEnum tokenType; + + public static OrchestrationEmbeddingRequest create( + OrchestrationEmbeddingModel model, List tokens) { + return new OrchestrationEmbeddingRequest(model, tokens, null, null); + } + + @Tolerate + @Nonnull + public OrchestrationEmbeddingRequest withMasking( + @Nonnull final MaskingProvider maskingProvider, + @Nonnull final MaskingProvider... maskingProviders) { + return withMasking(Lists.asList(maskingProvider, maskingProviders)); + } + + @Nonnull + public OrchestrationEmbeddingRequest asDocument() { + return withTokenType(DOCUMENT); + } + + @Nonnull + public OrchestrationEmbeddingRequest asText() { + return withTokenType(TEXT); + } + + @Nonnull + public OrchestrationEmbeddingRequest asQuery() { + return withTokenType(QUERY); + } + + EmbeddingsPostRequest createEmbeddingsPostRequest() { + + final var input = + EmbeddingsInput.create().text(EmbeddingsInputText.create(tokens)).type(tokenType); + final var embeddingsModelConfig = + EmbeddingsModelConfig.create().model(this.model.createEmbeddingsModelDetails()); + final var modules = + EmbeddingsOrchestrationConfig.create() + .modules(EmbeddingsModuleConfigs.create().embeddings(embeddingsModelConfig)); + + if (masking != null) { + final var dpiConfigs = this.masking.stream().map(MaskingProvider::createConfig).toList(); + modules.getModules().setMasking(MaskingModuleConfigProviders.create().providers(dpiConfigs)); + } + + return EmbeddingsPostRequest.create().config(modules).input(input); + } +} diff --git a/orchestration/src/test/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingTest.java b/orchestration/src/test/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingTest.java new file mode 100644 index 000000000..f92f1ceb4 --- /dev/null +++ b/orchestration/src/test/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingTest.java @@ -0,0 +1,77 @@ +package com.sap.ai.sdk.orchestration; + +import static com.sap.ai.sdk.orchestration.OrchestrationEmbeddingModel.TEXT_EMBEDDING_3_SMALL; +import static com.sap.ai.sdk.orchestration.model.EmbeddingsInput.TypeEnum.DOCUMENT; +import static com.sap.ai.sdk.orchestration.model.EmbeddingsModelParams.EncodingFormatEnum.BASE64; +import static org.assertj.core.api.Assertions.assertThat; + +import com.sap.ai.sdk.orchestration.model.DPIConfig; +import com.sap.ai.sdk.orchestration.model.DPIEntities; +import com.sap.ai.sdk.orchestration.model.DPIStandardEntity; +import com.sap.ai.sdk.orchestration.model.EmbeddingsInputText; +import com.sap.ai.sdk.orchestration.model.MaskingModuleConfigProviders; +import java.util.List; +import org.junit.jupiter.api.Test; + +class OrchestrationEmbeddingTest { + + @Test + void embeddingModelTest() { + final var model = TEXT_EMBEDDING_3_SMALL; + assertThat(model.name().equals("text-embedding-3-small")); + assertThat(model.version()).isNull(); + assertThat(model.dimensions()).isNull(); + assertThat(model.encodingFormat()).isNull(); + assertThat(model.normalize()).isNull(); + + final var model2 = + TEXT_EMBEDDING_3_SMALL + .withVersion("some-version") + .withDimensions(1536) + .withNormalize(true) + .withEncodingFormat(BASE64); + assertThat(model2.name().equals("text-embedding-3-large")); + assertThat(model2.version().equals("some-version")); + assertThat(model2.dimensions().equals(1536)); + assertThat(model2.normalize().equals(true)); + assertThat(model2.encodingFormat().equals(BASE64)); + + final var custom = new OrchestrationEmbeddingModel("custom-model"); + assertThat(custom.name()).isEqualTo("custom-model"); + } + + @Test + void embeddingRequestTest() { + final var request = + OrchestrationEmbeddingRequest.create(TEXT_EMBEDDING_3_SMALL, List.of("token1", "token2")) + .asDocument() + .withMasking( + DpiMasking.anonymization() + .withEntities(DPIEntities.ADDRESS) + .withAllowList(List.of("Alice"))); + + final var postRequest = request.createEmbeddingsPostRequest(); + assertThat(postRequest.getInput().getText()) + .isEqualTo(EmbeddingsInputText.create(List.of("token1", "token2"))); + assertThat(postRequest.getInput().getType()).isEqualTo(DOCUMENT); + final var embeddingsModelConfig = postRequest.getConfig().getModules().getEmbeddings(); + assertThat(embeddingsModelConfig.getModel().getName()).isEqualTo("text-embedding-3-small"); + assertThat(embeddingsModelConfig.getModel().getVersion()).isNull(); + assertThat(embeddingsModelConfig.getModel().getParams().getDimensions()).isNull(); + assertThat(embeddingsModelConfig.getModel().getParams().getEncodingFormat()).isNull(); + assertThat(embeddingsModelConfig.getModel().getParams().isNormalize()).isNull(); + + final var maskingConfig = postRequest.getConfig().getModules().getMasking(); + assertThat(maskingConfig) + .isInstanceOfSatisfying( + MaskingModuleConfigProviders.class, + cfg -> { + assertThat(cfg.getProviders()).hasSize(1); + assertThat(cfg.getProviders().get(0).getType()) + .isEqualTo(DPIConfig.TypeEnum.SAP_DATA_PRIVACY_INTEGRATION); + assertThat(cfg.getProviders().get(0).getEntities()) + .isEqualTo(List.of(DPIStandardEntity.create().type(DPIEntities.ADDRESS))); + assertThat(cfg.getProviders().get(0).getAllowlist()).isEqualTo(List.of("Alice")); + }); + } +} From af8ecda32268ef37c3e780c50a15eec748eaee93 Mon Sep 17 00:00:00 2001 From: Roshin Rajan Panackal Date: Fri, 29 Aug 2025 15:50:49 +0200 Subject: [PATCH 2/9] Finishing up all major items - client method for high level objects - Only support 'float' encoding format - Add javadoc - create convenience embedding response class - Add unit/integration tests - Add json payloads for req and res - Add e2e --- orchestration/pom.xml | 1 + .../orchestration/OrchestrationClient.java | 23 ++- .../OrchestrationEmbeddingModel.java | 40 ++-- .../OrchestrationEmbeddingRequest.java | 116 +++++++++-- .../OrchestrationEmbeddingResponse.java | 46 +++++ .../OrchestrationEmbeddingTest.java | 186 ++++++++++++------ .../resources/__files/embeddingResponse.json | 46 +++++ .../src/test/resources/embeddingRequest.json | 40 ++++ .../controllers/OrchestrationController.java | 10 + .../app/services/OrchestrationService.java | 26 +++ .../src/main/resources/static/index.html | 20 ++ .../app/controllers/OrchestrationTest.java | 12 ++ 12 files changed, 480 insertions(+), 86 deletions(-) create mode 100644 orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingResponse.java create mode 100644 orchestration/src/test/resources/__files/embeddingResponse.json create mode 100644 orchestration/src/test/resources/embeddingRequest.json diff --git a/orchestration/pom.xml b/orchestration/pom.xml index f87821a45..2a932f235 100644 --- a/orchestration/pom.xml +++ b/orchestration/pom.xml @@ -203,6 +203,7 @@ true true true + true diff --git a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationClient.java b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationClient.java index 988f39649..ec70ae6e3 100644 --- a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationClient.java +++ b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationClient.java @@ -218,12 +218,31 @@ public Stream streamChatCompletionDeltas( } /** - * Generate embeddings for the given request. + * Generate embeddings for a {@code OrchestrationEmbeddingRequest} request. * * @param request the request containing the input text and other parameters. * @return the response containing the embeddings. * @throws OrchestrationClientException if the request fails - * @since 1.9.0 + * @since 1.11.0 + */ + @Nonnull + public OrchestrationEmbeddingResponse embed(@Nonnull final OrchestrationEmbeddingRequest request) + throws OrchestrationClientException { + final var response = embed(request.createEmbeddingsPostRequest()); + return new OrchestrationEmbeddingResponse(response); + } + + /** + * Generates embeddings using the low-level API request. + * + *

This method provides direct access to the underlying API for advanced use cases. For most + * scenarios, prefer {@link #embed(OrchestrationEmbeddingRequest)}. + * + * @param request the low-level API request + * @return the low level response object + * @throws OrchestrationClientException if the request fails + * @since 1.11.0 + * @see #embed(OrchestrationEmbeddingRequest) */ @Nonnull public EmbeddingsPostResponse embed(@Nonnull final EmbeddingsPostRequest request) diff --git a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingModel.java b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingModel.java index f146aebdd..8ef02e93c 100644 --- a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingModel.java +++ b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingModel.java @@ -4,6 +4,7 @@ import com.sap.ai.sdk.core.AiModel; import com.sap.ai.sdk.orchestration.model.EmbeddingsModelDetails; import com.sap.ai.sdk.orchestration.model.EmbeddingsModelParams; +import com.sap.ai.sdk.orchestration.model.EmbeddingsModelParams.EncodingFormatEnum; import javax.annotation.Nonnull; import javax.annotation.Nullable; import lombok.AccessLevel; @@ -12,46 +13,61 @@ import lombok.With; import lombok.experimental.Accessors; -// ideally this is a record but exposes all args constructor which we want to avoid. Is it worth a -// value class? -// Currently, model list follow SAP Notes as the source of truth. But deprecated models are not -// listed there. -// Can we reuse existing enum from generated class? +/** + * Configuration for embedding models in the Orchestration service. + * + * @since 1.11.0 + */ @Beta @With @Value @Accessors(fluent = true) @AllArgsConstructor(access = AccessLevel.PRIVATE) public class OrchestrationEmbeddingModel implements AiModel { + /** The name of the embedding model. */ @Nonnull String name; + + /** The version of the model, defaults to latest if not specified. */ @Nullable String version; + + /** The number of dimensions for the output embeddings. */ @Nullable Integer dimensions; - @Nullable Boolean normalize; - @Nullable EmbeddingsModelParams.EncodingFormatEnum encodingFormat; - public OrchestrationEmbeddingModel(@Nonnull final String name) { - this(name, null, null, null, null); - } + /** Whether to normalize the embedding vectors. */ + @Nullable Boolean normalize; + /** Azure OpenAI Text Embedding 3 Small model */ public static final OrchestrationEmbeddingModel TEXT_EMBEDDING_3_SMALL = new OrchestrationEmbeddingModel("text-embedding-3-small"); + /** Azure OpenAI Text Embedding 3 Large model */ public static final OrchestrationEmbeddingModel TEXT_EMBEDDING_3_LARGE = new OrchestrationEmbeddingModel("text-embedding-3-large"); + /** Amazon Titan Embed Text model */ public static final OrchestrationEmbeddingModel AMAZON_TITAN_EMBED_TEXT = new OrchestrationEmbeddingModel("amazon.titan-embed-text"); + /** NVIDIA LLaMA 3.2 7B NV EmbedQA model */ public static final OrchestrationEmbeddingModel NVIDIA_LLAMA_32_NV_EMBEDQA_1B = new OrchestrationEmbeddingModel("nvidia--llama-3.2-nv-embedqa-1b"); - EmbeddingsModelDetails createEmbeddingsModelDetails() { + /** + * Creates a new embedding model configuration with the specified name. + * + * @param name the model name + */ + public OrchestrationEmbeddingModel(@Nonnull final String name) { + this(name, null, null, null); + } + @Nonnull + EmbeddingsModelDetails createEmbeddingsModelDetails() { final var params = EmbeddingsModelParams.create() .dimensions(dimensions) .normalize(normalize) - .encodingFormat(encodingFormat); + .encodingFormat(EncodingFormatEnum.FLOAT); return EmbeddingsModelDetails.create().name(name).version(version).params(params); } } diff --git a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingRequest.java b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingRequest.java index 908107726..b037d90cd 100644 --- a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingRequest.java +++ b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingRequest.java @@ -1,8 +1,8 @@ package com.sap.ai.sdk.orchestration; -import static com.sap.ai.sdk.orchestration.model.EmbeddingsInput.TypeEnum.DOCUMENT; -import static com.sap.ai.sdk.orchestration.model.EmbeddingsInput.TypeEnum.QUERY; -import static com.sap.ai.sdk.orchestration.model.EmbeddingsInput.TypeEnum.TEXT; +import static com.sap.ai.sdk.orchestration.OrchestrationEmbeddingRequest.TokenType.DOCUMENT; +import static com.sap.ai.sdk.orchestration.OrchestrationEmbeddingRequest.TokenType.QUERY; +import static com.sap.ai.sdk.orchestration.OrchestrationEmbeddingRequest.TokenType.TEXT; import static lombok.AccessLevel.PRIVATE; import com.google.common.annotations.Beta; @@ -18,33 +18,86 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.RequiredArgsConstructor; import lombok.Value; import lombok.With; import lombok.experimental.Tolerate; -// Do we need staged input builder here? -// Do we need an enum for tokenType? +/** + * Represents a request for generating embeddings through the SAP AI Core Orchestration service. + * + * @since 1.11.0 + */ @Beta @Value @AllArgsConstructor(access = PRIVATE) public class OrchestrationEmbeddingRequest { + /** The embedding model to use for generating vector representations. */ @Nonnull OrchestrationEmbeddingModel model; + + /** The list of text inputs to be converted into embeddings. */ @Nonnull List tokens; + /** Optional masking providers for data privacy and security. */ @With(value = PRIVATE) @Nullable List masking; + /** Optional token type classification to optimize embedding generation. */ @With(value = PRIVATE) @Nullable - EmbeddingsInput.TypeEnum tokenType; + TokenType tokenType; + + /** + * Create an embedding request using fluent API starting with model selection. + * + *

{@code
+   * OrchestrationEmbeddingRequest.forModel(myModel).forInputs("text to embed");
+   * }
+ * + * @param model the embedding model to use + * @return a step for specifying inputs + */ + @Nonnull + public static InputStep forModel(@Nonnull final OrchestrationEmbeddingModel model) { + return tokens -> new OrchestrationEmbeddingRequest(model, List.copyOf(tokens), null, null); + } - public static OrchestrationEmbeddingRequest create( - OrchestrationEmbeddingModel model, List tokens) { - return new OrchestrationEmbeddingRequest(model, tokens, null, null); + /** Builder step for specifying text inputs to embed. */ + @FunctionalInterface + public interface InputStep { + + /** + * Specifies text inputs to be embedded. + * + * @param tokens the text strings to embed + * @return a new embedding request instance + */ + @Nonnull + OrchestrationEmbeddingRequest forInputs(@Nonnull final List tokens); + + /** + * Specifies multiple text inputs using variable arguments. + * + * @param tokens one or more strings to embed + * @return a new embedding request instance + */ + @Nonnull + default OrchestrationEmbeddingRequest forInputs(@Nonnull final String... tokens) { + return forInputs(List.of(tokens)); + } } + /** + * Adds data masking providers to enable detection and masking of sensitive information. + * + * @param maskingProvider the primary masking provider + * @param maskingProviders additional masking providers + * @return a new request instance with the specified masking providers + * @see MaskingProvider + */ @Tolerate @Nonnull public OrchestrationEmbeddingRequest withMasking( @@ -53,36 +106,71 @@ public OrchestrationEmbeddingRequest withMasking( return withMasking(Lists.asList(maskingProvider, maskingProviders)); } + /** + * Configures this request to optimize embeddings for document content. + * + * @return a new request instance configured for document embedding + */ @Nonnull public OrchestrationEmbeddingRequest asDocument() { return withTokenType(DOCUMENT); } + /** + * Configures this request to optimize embeddings for general text content. + * + * @return a new request instance configured for text embedding + */ @Nonnull public OrchestrationEmbeddingRequest asText() { return withTokenType(TEXT); } + /** + * Configures this request to optimize embeddings for query content. + * + * @return a new request instance configured for query embedding + */ @Nonnull public OrchestrationEmbeddingRequest asQuery() { return withTokenType(QUERY); } + @Nonnull EmbeddingsPostRequest createEmbeddingsPostRequest() { - final var input = - EmbeddingsInput.create().text(EmbeddingsInputText.create(tokens)).type(tokenType); + final var input = EmbeddingsInput.create().text(EmbeddingsInputText.create(tokens)); final var embeddingsModelConfig = - EmbeddingsModelConfig.create().model(this.model.createEmbeddingsModelDetails()); + EmbeddingsModelConfig.create().model(model.createEmbeddingsModelDetails()); final var modules = EmbeddingsOrchestrationConfig.create() .modules(EmbeddingsModuleConfigs.create().embeddings(embeddingsModelConfig)); + if (tokenType != null) { + input.setType(EmbeddingsInput.TypeEnum.fromValue(tokenType.getValue())); + } if (masking != null) { - final var dpiConfigs = this.masking.stream().map(MaskingProvider::createConfig).toList(); + final var dpiConfigs = masking.stream().map(MaskingProvider::createConfig).toList(); modules.getModules().setMasking(MaskingModuleConfigProviders.create().providers(dpiConfigs)); } - return EmbeddingsPostRequest.create().config(modules).input(input); } + + /** + * Token type classification for optimizing embedding generation. + * + *

Token types may influence how the embedding model processes and represents the input text. + */ + @Getter + @RequiredArgsConstructor + public enum TokenType { + /** For document content. */ + DOCUMENT("document"), + /** For general text (default). */ + TEXT("text"), + /** For search queries. */ + QUERY("query"); + + private final String value; + } } diff --git a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingResponse.java b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingResponse.java new file mode 100644 index 000000000..bea05225a --- /dev/null +++ b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingResponse.java @@ -0,0 +1,46 @@ +package com.sap.ai.sdk.orchestration; + +import static lombok.AccessLevel.PACKAGE; + +import com.google.common.annotations.Beta; +import com.sap.ai.sdk.orchestration.model.Embedding; +import com.sap.ai.sdk.orchestration.model.EmbeddingsPostResponse; +import java.util.ArrayList; +import java.util.List; +import javax.annotation.Nonnull; +import lombok.AllArgsConstructor; +import lombok.Value; + +/** + * Response wrapper for orchestration embedding operations. + * + *

Wraps {@link EmbeddingsPostResponse} and provides convenient access to embedding vectors. + */ +@Beta +@Value +@AllArgsConstructor(access = PACKAGE) +public class OrchestrationEmbeddingResponse { + + /** The original embedding response from the orchestration API. */ + @Nonnull EmbeddingsPostResponse originalResponse; + + /** + * Extracts embedding vectors as float arrays. + * + * @return list of embedding vectors, never {@code null} + */ + @Nonnull + public List getEmbeddingVectors() { + final var embeddings = new ArrayList(); + for (final var container : originalResponse.getFinalResult().getData()) { + final var bigDecimals = (Embedding.InnerBigDecimals) container.getEmbedding(); + final var values = bigDecimals.values(); + final float[] arr = new float[values.size()]; + for (int i = 0; i < values.size(); i++) { + arr[i] = values.get(i).floatValue(); + } + embeddings.add(arr); + } + return embeddings; + } +} diff --git a/orchestration/src/test/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingTest.java b/orchestration/src/test/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingTest.java index f92f1ceb4..5377f6802 100644 --- a/orchestration/src/test/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingTest.java +++ b/orchestration/src/test/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingTest.java @@ -1,77 +1,147 @@ package com.sap.ai.sdk.orchestration; +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; +import static com.github.tomakehurst.wiremock.client.WireMock.verify; import static com.sap.ai.sdk.orchestration.OrchestrationEmbeddingModel.TEXT_EMBEDDING_3_SMALL; +import static com.sap.ai.sdk.orchestration.model.DPIEntities.PERSON; +import static com.sap.ai.sdk.orchestration.model.EmbeddingResult.ObjectEnum.EMBEDDING; import static com.sap.ai.sdk.orchestration.model.EmbeddingsInput.TypeEnum.DOCUMENT; -import static com.sap.ai.sdk.orchestration.model.EmbeddingsModelParams.EncodingFormatEnum.BASE64; +import static com.sap.ai.sdk.orchestration.model.EmbeddingsInput.TypeEnum.QUERY; +import static com.sap.ai.sdk.orchestration.model.EmbeddingsInput.TypeEnum.TEXT; +import static com.sap.ai.sdk.orchestration.model.EmbeddingsResponse.ObjectEnum.LIST; import static org.assertj.core.api.Assertions.assertThat; -import com.sap.ai.sdk.orchestration.model.DPIConfig; -import com.sap.ai.sdk.orchestration.model.DPIEntities; -import com.sap.ai.sdk.orchestration.model.DPIStandardEntity; -import com.sap.ai.sdk.orchestration.model.EmbeddingsInputText; -import com.sap.ai.sdk.orchestration.model.MaskingModuleConfigProviders; +import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; +import com.github.tomakehurst.wiremock.junit5.WireMockTest; +import com.sap.ai.sdk.orchestration.model.EmbeddingsModelParams.EncodingFormatEnum; +import com.sap.cloud.sdk.cloudplatform.connectivity.ApacheHttpClient5Accessor; +import com.sap.cloud.sdk.cloudplatform.connectivity.ApacheHttpClient5Cache; +import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination; +import java.io.InputStream; import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; +import lombok.SneakyThrows; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +@WireMockTest class OrchestrationEmbeddingTest { + private static OrchestrationClient client; + + private final Function fileLoader = + filename -> Objects.requireNonNull(getClass().getClassLoader().getResourceAsStream(filename)); + + @BeforeEach + void setup(WireMockRuntimeInfo server) { + final DefaultHttpDestination destination = + DefaultHttpDestination.builder(server.getHttpBaseUrl()).build(); + client = new OrchestrationClient(destination); + ApacheHttpClient5Accessor.setHttpClientCache(ApacheHttpClient5Cache.DISABLED); + } + + @AfterEach + void reset() { + ApacheHttpClient5Accessor.setHttpClientCache(null); + ApacheHttpClient5Accessor.setHttpClientFactory(null); + } @Test - void embeddingModelTest() { - final var model = TEXT_EMBEDDING_3_SMALL; - assertThat(model.name().equals("text-embedding-3-small")); - assertThat(model.version()).isNull(); - assertThat(model.dimensions()).isNull(); - assertThat(model.encodingFormat()).isNull(); - assertThat(model.normalize()).isNull(); - - final var model2 = - TEXT_EMBEDDING_3_SMALL - .withVersion("some-version") - .withDimensions(1536) - .withNormalize(true) - .withEncodingFormat(BASE64); - assertThat(model2.name().equals("text-embedding-3-large")); - assertThat(model2.version().equals("some-version")); - assertThat(model2.dimensions().equals(1536)); - assertThat(model2.normalize().equals(true)); - assertThat(model2.encodingFormat().equals(BASE64)); - - final var custom = new OrchestrationEmbeddingModel("custom-model"); - assertThat(custom.name()).isEqualTo("custom-model"); + void testEmbeddingModel() { + final var model = + TEXT_EMBEDDING_3_SMALL.withVersion("v1").withDimensions(1536).withNormalize(true); + + final var lowLevelModel1 = model.createEmbeddingsModelDetails(); + assertThat(lowLevelModel1.getName()).isEqualTo("text-embedding-3-small"); + assertThat(lowLevelModel1.getVersion()).isEqualTo("v1"); + assertThat(lowLevelModel1.getParams().getDimensions()).isEqualTo(1536); + assertThat(lowLevelModel1.getParams().isNormalize()).isTrue(); + assertThat(lowLevelModel1.getParams().getEncodingFormat()).isEqualTo(EncodingFormatEnum.FLOAT); + + final var model2 = new OrchestrationEmbeddingModel("custom-model"); + final var lowLevelModel2 = model2.createEmbeddingsModelDetails(); + assertThat(lowLevelModel2.getName()).isEqualTo("custom-model"); } @Test - void embeddingRequestTest() { + void testEmbeddingRequestTokenTypes() { + var request = + OrchestrationEmbeddingRequest.forModel(TEXT_EMBEDDING_3_SMALL).forInputs("Hello World"); + + request = request.asText(); + var lowLevelRequest = request.createEmbeddingsPostRequest(); + assertThat(lowLevelRequest.getInput().getType()).isEqualTo(TEXT); + + request = request.asDocument(); + lowLevelRequest = request.createEmbeddingsPostRequest(); + assertThat(lowLevelRequest.getInput().getType()).isEqualTo(DOCUMENT); + + request = request.asQuery(); + lowLevelRequest = request.createEmbeddingsPostRequest(); + assertThat(lowLevelRequest.getInput().getType()).isEqualTo(QUERY); + } + + @SneakyThrows + @Test + void testEmbeddingRequest() { + stubFor( + post(urlPathEqualTo("/v2/embeddings")) + .withHeader("Content-Type", equalTo("application/json; charset=UTF-8")) + .willReturn( + aResponse() + .withBodyFile("embeddingResponse.json") + .withHeader("Content-Type", "application/json"))); + + final var masking = + DpiMasking.anonymization().withEntities(PERSON).withAllowList(List.of("SAP", "Joule")); final var request = - OrchestrationEmbeddingRequest.create(TEXT_EMBEDDING_3_SMALL, List.of("token1", "token2")) - .asDocument() - .withMasking( - DpiMasking.anonymization() - .withEntities(DPIEntities.ADDRESS) - .withAllowList(List.of("Alice"))); - - final var postRequest = request.createEmbeddingsPostRequest(); - assertThat(postRequest.getInput().getText()) - .isEqualTo(EmbeddingsInputText.create(List.of("token1", "token2"))); - assertThat(postRequest.getInput().getType()).isEqualTo(DOCUMENT); - final var embeddingsModelConfig = postRequest.getConfig().getModules().getEmbeddings(); - assertThat(embeddingsModelConfig.getModel().getName()).isEqualTo("text-embedding-3-small"); - assertThat(embeddingsModelConfig.getModel().getVersion()).isNull(); - assertThat(embeddingsModelConfig.getModel().getParams().getDimensions()).isNull(); - assertThat(embeddingsModelConfig.getModel().getParams().getEncodingFormat()).isNull(); - assertThat(embeddingsModelConfig.getModel().getParams().isNormalize()).isNull(); - - final var maskingConfig = postRequest.getConfig().getModules().getMasking(); - assertThat(maskingConfig) - .isInstanceOfSatisfying( - MaskingModuleConfigProviders.class, - cfg -> { - assertThat(cfg.getProviders()).hasSize(1); - assertThat(cfg.getProviders().get(0).getType()) - .isEqualTo(DPIConfig.TypeEnum.SAP_DATA_PRIVACY_INTEGRATION); - assertThat(cfg.getProviders().get(0).getEntities()) - .isEqualTo(List.of(DPIStandardEntity.create().type(DPIEntities.ADDRESS))); - assertThat(cfg.getProviders().get(0).getAllowlist()).isEqualTo(List.of("Alice")); + OrchestrationEmbeddingRequest.forModel(TEXT_EMBEDDING_3_SMALL) + .forInputs(List.of("Hi SAP Orchestration Service", "I am John Doe")) + .withMasking(masking); + + final var response = client.embed(request); + + assertThat(response.getEmbeddingVectors()) + .isNotNull() + .hasSize(2) + .isInstanceOf(List.class) + .contains( + new float[] {-0.09068491f, -0.3462946f, 0.88297224f, -0.29537824f, 0.0704844f}, + new float[] {0.18703203f, -0.10362422f, -0.65176725f, 0.6386932f, -0.34864223f}); + + assertThat(response.getOriginalResponse().getRequestId()) + .isEqualTo("62935941-7c2d-4c16-8a35-0b9dce7c8c9e"); + assertThat(response.getOriginalResponse().getIntermediateResults().getInputMasking().getData()) + .isEqualTo( + Map.of("masked_input", List.of("Hi SAP Orchestration Service", "I am MASKED_PERSON"))); + final var finalResult = response.getOriginalResponse().getFinalResult(); + assertThat(finalResult.getModel()).isEqualTo("text-embedding-3-small"); + + assertThat(finalResult.getUsage().getPromptTokens()).isEqualTo(11); + assertThat(finalResult.getUsage().getTotalTokens()).isEqualTo(11); + assertThat(finalResult.getObject()).isEqualTo(LIST); + assertThat(finalResult.getData()) + .hasSize(2) + .allSatisfy( + embeddingData -> { + assertThat(embeddingData.getObject()).isEqualTo(EMBEDDING); + assertThat(embeddingData.getEmbedding()).isNotNull(); + assertThat(embeddingData.getIndex()).isIn(0, 1); }); + + try (var inputStream = fileLoader.apply("embeddingRequest.json")) { + var requestJson = new String(inputStream.readAllBytes()); + verify( + postRequestedFor(urlEqualTo("/v2/embeddings")).withRequestBody(equalToJson(requestJson))); + } } } diff --git a/orchestration/src/test/resources/__files/embeddingResponse.json b/orchestration/src/test/resources/__files/embeddingResponse.json new file mode 100644 index 000000000..8b6ca278c --- /dev/null +++ b/orchestration/src/test/resources/__files/embeddingResponse.json @@ -0,0 +1,46 @@ +{ + "request_id": "62935941-7c2d-4c16-8a35-0b9dce7c8c9e", + "intermediate_results": { + "input_masking": { + "message": "Embedding input is masked successfully.", + "data": { + "masked_input": [ + "Hi SAP Orchestration Service", + "I am MASKED_PERSON" + ] + } + } + }, + "final_result": { + "object": "list", + "data": [ + { + "object": "embedding", + "embedding": [ + -0.09068491, + -0.3462946, + 0.88297224, + -0.29537824, + 0.0704844 + ], + "index": 0 + }, + { + "object": "embedding", + "embedding": [ + 0.18703203, + -0.10362422, + -0.65176725, + 0.6386932, + -0.34864223 + ], + "index": 1 + } + ], + "model": "text-embedding-3-small", + "usage": { + "prompt_tokens": 11, + "total_tokens": 11 + } + } +} diff --git a/orchestration/src/test/resources/embeddingRequest.json b/orchestration/src/test/resources/embeddingRequest.json new file mode 100644 index 000000000..92b35281e --- /dev/null +++ b/orchestration/src/test/resources/embeddingRequest.json @@ -0,0 +1,40 @@ +{ + "config": { + "modules": { + "embeddings": { + "model": { + "name": "text-embedding-3-small", + "params": { + "encoding_format": "float" + } + } + }, + "masking": { + "providers": [ + { + "type": "sap_data_privacy_integration", + "method": "anonymization", + "entities": [ + { + "type": "profile-person" + } + ], + "allowlist": [ + "SAP", + "Joule" + ], + "mask_grounding_input": { + "enabled": false + } + } + ] + } + } + }, + "input": { + "text": [ + "Hi SAP Orchestration Service", + "I am John Doe" + ] + } +} \ No newline at end of file diff --git a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/controllers/OrchestrationController.java b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/controllers/OrchestrationController.java index 6f1d48b1f..43281d59d 100644 --- a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/controllers/OrchestrationController.java +++ b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/controllers/OrchestrationController.java @@ -340,4 +340,14 @@ Object translation(@RequestParam(value = "format", required = false) final Strin } return response.getContent(); } + + @GetMapping("/embedding") + @Nonnull + Object embedding(@RequestParam(value = "format", required = false) final String format) { + final var response = service.embed(List.of("Hi SAP Orchestration Service", "I am John Doe")); + if ("json".equals(format)) { + return response; + } + return response.getEmbeddingVectors(); + } } diff --git a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/OrchestrationService.java b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/OrchestrationService.java index 8d124b7ac..c014398ff 100644 --- a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/OrchestrationService.java +++ b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/OrchestrationService.java @@ -3,6 +3,7 @@ import static com.sap.ai.sdk.orchestration.OrchestrationAiModel.GEMINI_2_5_FLASH; import static com.sap.ai.sdk.orchestration.OrchestrationAiModel.GPT_4O_MINI; import static com.sap.ai.sdk.orchestration.OrchestrationAiModel.Parameter.TEMPERATURE; +import static com.sap.ai.sdk.orchestration.OrchestrationEmbeddingModel.TEXT_EMBEDDING_3_SMALL; import static com.sap.ai.sdk.orchestration.model.SAPDocumentTranslation.TypeEnum.SAP_DOCUMENT_TRANSLATION; import com.fasterxml.jackson.annotation.JsonProperty; @@ -17,6 +18,8 @@ import com.sap.ai.sdk.orchestration.OrchestrationChatResponse; import com.sap.ai.sdk.orchestration.OrchestrationClient; import com.sap.ai.sdk.orchestration.OrchestrationClientException; +import com.sap.ai.sdk.orchestration.OrchestrationEmbeddingRequest; +import com.sap.ai.sdk.orchestration.OrchestrationEmbeddingResponse; import com.sap.ai.sdk.orchestration.OrchestrationModuleConfig; import com.sap.ai.sdk.orchestration.OrchestrationPrompt; import com.sap.ai.sdk.orchestration.ResponseJsonSchema; @@ -597,4 +600,27 @@ public OrchestrationChatResponse translation() { return client.chatCompletion(prompt, configWithTranslation); } + + /** + * Create text embeddings using the Orchestration service. + * + * @link + * AI Core: Orchestration - Embedding + * @param texts the list of texts to embed + * @return the embedding response object + */ + @Nonnull + public OrchestrationEmbeddingResponse embed(@Nonnull final List texts) { + final var masking = + DpiMasking.anonymization() + .withEntities(DPIEntities.PERSON) + .withAllowList(List.of("SAP", "Joule")); + + final var request = + OrchestrationEmbeddingRequest.forModel(TEXT_EMBEDDING_3_SMALL) + .forInputs(texts) + .withMasking(masking); + return client.embed(request); + } } diff --git a/sample-code/spring-app/src/main/resources/static/index.html b/sample-code/spring-app/src/main/resources/static/index.html index 62a5f9052..7474dc579 100644 --- a/sample-code/spring-app/src/main/resources/static/index.html +++ b/sample-code/spring-app/src/main/resources/static/index.html @@ -557,6 +557,26 @@

Orchestration

+
+
+
Embedding
+
+
    +
  • +
    + +
    + Get the embedding of a text after masking using the Orchestration + service. +
    +
    +
  • +
+
diff --git a/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/OrchestrationTest.java b/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/OrchestrationTest.java index ee4bc6c94..68dfa40da 100644 --- a/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/OrchestrationTest.java +++ b/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/OrchestrationTest.java @@ -458,4 +458,16 @@ void testTranslation() { assertThat(inputTranslation.getMessage()).isEqualTo("Input to LLM is translated successfully."); assertThat(outputTranslation.getMessage()).isEqualTo("Output Translation successful"); } + + @Test + void testEmbedding() { + val result = service.embed(List.of("Hi SAP Orchestration Service", "I am John Doe")); + val embeddingVectors = result.getEmbeddingVectors(); + + assertThat(embeddingVectors) + .isNotNull() + .hasSize(2) + .isInstanceOf(List.class) + .allSatisfy(vector -> assertThat(vector).isInstanceOf(float[].class)); + } } From 3e6a5624ff22feec04b34d75761c3d5227a2b6ac Mon Sep 17 00:00:00 2001 From: Roshin Rajan Panackal Date: Fri, 29 Aug 2025 16:31:07 +0200 Subject: [PATCH 3/9] Add release notes and clean up --- docs/release_notes.md | 5 ++++- orchestration/pom.xml | 1 - .../ai/sdk/orchestration/OrchestrationEmbeddingResponse.java | 2 ++ .../com/sap/ai/sdk/app/services/OrchestrationService.java | 5 ++--- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/release_notes.md b/docs/release_notes.md index 6d043220b..d385f9f84 100644 --- a/docs/release_notes.md +++ b/docs/release_notes.md @@ -27,7 +27,10 @@ - Added `OpenAiChatModel` - [Prompt Registry] [Using Prompt Registry Templates in SpringAI.](https://sap.github.io/ai-sdk/docs/java/ai-core/prompt-registry#using-templates-in-springai) - Added `SpringAiConverter` - +- [Orchestration] Added embedding generation support with new `OrchestrationClient#embed()` methods. + - Added `OrchestrationEmbeddingModel` with `TEXT_EMBEDDING_3_SMALL`, `TEXT_EMBEDDING_3_LARGE`, `AMAZON_TITAN_EMBED_TEXT` and `NVIDIA_LLAMA_32_NV_EMBEDQA_1B` embedding models. + - Introduced `OrchestrationEmbeddingRequest` for building requests fluently and `OrchestrationEmbeddingResponse#getEmbeddingVectors()` to retrieve embeddings. + ### 📈 Improvements - diff --git a/orchestration/pom.xml b/orchestration/pom.xml index 2a932f235..f87821a45 100644 --- a/orchestration/pom.xml +++ b/orchestration/pom.xml @@ -203,7 +203,6 @@ true true true - true diff --git a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingResponse.java b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingResponse.java index bea05225a..e6fd16ff7 100644 --- a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingResponse.java +++ b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingResponse.java @@ -15,6 +15,8 @@ * Response wrapper for orchestration embedding operations. * *

Wraps {@link EmbeddingsPostResponse} and provides convenient access to embedding vectors. + * + * @since 1.11.0 */ @Beta @Value diff --git a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/OrchestrationService.java b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/OrchestrationService.java index c014398ff..bd04bb39d 100644 --- a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/OrchestrationService.java +++ b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/OrchestrationService.java @@ -604,9 +604,8 @@ public OrchestrationChatResponse translation() { /** * Create text embeddings using the Orchestration service. * - * @link - * AI Core: Orchestration - Embedding + * @link AI + * Core: Orchestration - Embedding * @param texts the list of texts to embed * @return the embedding response object */ From d7ea81cfad0d9203b65b54c845de308d951d70e1 Mon Sep 17 00:00:00 2001 From: Roshin Rajan Panackal Date: Thu, 4 Sep 2025 14:29:06 +0200 Subject: [PATCH 4/9] Remove enum and remove redundant test --- .../OrchestrationEmbeddingRequest.java | 38 +--- .../OrchestrationEmbeddingTest.java | 12 +- .../orchestration/OrchestrationUnitTest.java | 166 ------------------ 3 files changed, 19 insertions(+), 197 deletions(-) diff --git a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingRequest.java b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingRequest.java index b037d90cd..9367618c3 100644 --- a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingRequest.java +++ b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingRequest.java @@ -1,8 +1,6 @@ package com.sap.ai.sdk.orchestration; -import static com.sap.ai.sdk.orchestration.OrchestrationEmbeddingRequest.TokenType.DOCUMENT; -import static com.sap.ai.sdk.orchestration.OrchestrationEmbeddingRequest.TokenType.QUERY; -import static com.sap.ai.sdk.orchestration.OrchestrationEmbeddingRequest.TokenType.TEXT; +import static lombok.AccessLevel.NONE; import static lombok.AccessLevel.PRIVATE; import com.google.common.annotations.Beta; @@ -19,7 +17,6 @@ import javax.annotation.Nullable; import lombok.AllArgsConstructor; import lombok.Getter; -import lombok.RequiredArgsConstructor; import lombok.Value; import lombok.With; import lombok.experimental.Tolerate; @@ -47,8 +44,9 @@ public class OrchestrationEmbeddingRequest { /** Optional token type classification to optimize embedding generation. */ @With(value = PRIVATE) + @Getter(NONE) @Nullable - TokenType tokenType; + EmbeddingsInput.TypeEnum tokenType; /** * Create an embedding request using fluent API starting with model selection. @@ -113,7 +111,7 @@ public OrchestrationEmbeddingRequest withMasking( */ @Nonnull public OrchestrationEmbeddingRequest asDocument() { - return withTokenType(DOCUMENT); + return withTokenType(EmbeddingsInput.TypeEnum.DOCUMENT); } /** @@ -123,7 +121,7 @@ public OrchestrationEmbeddingRequest asDocument() { */ @Nonnull public OrchestrationEmbeddingRequest asText() { - return withTokenType(TEXT); + return withTokenType(EmbeddingsInput.TypeEnum.TEXT); } /** @@ -133,44 +131,24 @@ public OrchestrationEmbeddingRequest asText() { */ @Nonnull public OrchestrationEmbeddingRequest asQuery() { - return withTokenType(QUERY); + return withTokenType(EmbeddingsInput.TypeEnum.QUERY); } @Nonnull EmbeddingsPostRequest createEmbeddingsPostRequest() { - final var input = EmbeddingsInput.create().text(EmbeddingsInputText.create(tokens)); + final var input = + EmbeddingsInput.create().text(EmbeddingsInputText.create(tokens)).type(tokenType); final var embeddingsModelConfig = EmbeddingsModelConfig.create().model(model.createEmbeddingsModelDetails()); final var modules = EmbeddingsOrchestrationConfig.create() .modules(EmbeddingsModuleConfigs.create().embeddings(embeddingsModelConfig)); - if (tokenType != null) { - input.setType(EmbeddingsInput.TypeEnum.fromValue(tokenType.getValue())); - } if (masking != null) { final var dpiConfigs = masking.stream().map(MaskingProvider::createConfig).toList(); modules.getModules().setMasking(MaskingModuleConfigProviders.create().providers(dpiConfigs)); } return EmbeddingsPostRequest.create().config(modules).input(input); } - - /** - * Token type classification for optimizing embedding generation. - * - *

Token types may influence how the embedding model processes and represents the input text. - */ - @Getter - @RequiredArgsConstructor - public enum TokenType { - /** For document content. */ - DOCUMENT("document"), - /** For general text (default). */ - TEXT("text"), - /** For search queries. */ - QUERY("query"); - - private final String value; - } } diff --git a/orchestration/src/test/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingTest.java b/orchestration/src/test/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingTest.java index 5377f6802..0f88cd492 100644 --- a/orchestration/src/test/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingTest.java +++ b/orchestration/src/test/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingTest.java @@ -123,9 +123,9 @@ void testEmbeddingRequest() { assertThat(response.getOriginalResponse().getIntermediateResults().getInputMasking().getData()) .isEqualTo( Map.of("masked_input", List.of("Hi SAP Orchestration Service", "I am MASKED_PERSON"))); + final var finalResult = response.getOriginalResponse().getFinalResult(); assertThat(finalResult.getModel()).isEqualTo("text-embedding-3-small"); - assertThat(finalResult.getUsage().getPromptTokens()).isEqualTo(11); assertThat(finalResult.getUsage().getTotalTokens()).isEqualTo(11); assertThat(finalResult.getObject()).isEqualTo(LIST); @@ -138,6 +138,16 @@ void testEmbeddingRequest() { assertThat(embeddingData.getIndex()).isIn(0, 1); }); + final var moduleResults = response.getOriginalResponse().getIntermediateResults(); + assertThat(moduleResults).isNotNull(); + assertThat(moduleResults.getInputMasking()).isNotNull(); + assertThat(moduleResults.getInputMasking().getMessage()) + .isEqualTo("Embedding input is masked successfully."); + assertThat(moduleResults.getInputMasking().getData()).isNotNull(); + assertThat(moduleResults.getInputMasking().getData()) + .isEqualTo( + Map.of("masked_input", List.of("Hi SAP Orchestration Service", "I am MASKED_PERSON"))); + try (var inputStream = fileLoader.apply("embeddingRequest.json")) { var requestJson = new String(inputStream.readAllBytes()); verify( diff --git a/orchestration/src/test/java/com/sap/ai/sdk/orchestration/OrchestrationUnitTest.java b/orchestration/src/test/java/com/sap/ai/sdk/orchestration/OrchestrationUnitTest.java index df9be4417..ba7ae596d 100644 --- a/orchestration/src/test/java/com/sap/ai/sdk/orchestration/OrchestrationUnitTest.java +++ b/orchestration/src/test/java/com/sap/ai/sdk/orchestration/OrchestrationUnitTest.java @@ -12,7 +12,6 @@ import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; import static com.github.tomakehurst.wiremock.client.WireMock.serverError; import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; import static com.github.tomakehurst.wiremock.client.WireMock.verify; import static com.sap.ai.sdk.orchestration.AzureFilterThreshold.ALLOW_SAFE; @@ -42,22 +41,9 @@ import com.github.tomakehurst.wiremock.junit5.WireMockTest; import com.github.tomakehurst.wiremock.stubbing.Scenario; import com.sap.ai.sdk.orchestration.model.ChatDelta; -import com.sap.ai.sdk.orchestration.model.DPIConfig; import com.sap.ai.sdk.orchestration.model.DPIEntities; -import com.sap.ai.sdk.orchestration.model.DPIStandardEntity; import com.sap.ai.sdk.orchestration.model.DataRepositoryType; import com.sap.ai.sdk.orchestration.model.DocumentGroundingFilter; -import com.sap.ai.sdk.orchestration.model.Embedding; -import com.sap.ai.sdk.orchestration.model.EmbeddingsInput; -import com.sap.ai.sdk.orchestration.model.EmbeddingsInputText; -import com.sap.ai.sdk.orchestration.model.EmbeddingsModelConfig; -import com.sap.ai.sdk.orchestration.model.EmbeddingsModelDetails; -import com.sap.ai.sdk.orchestration.model.EmbeddingsModelParams; -import com.sap.ai.sdk.orchestration.model.EmbeddingsModuleConfigs; -import com.sap.ai.sdk.orchestration.model.EmbeddingsOrchestrationConfig; -import com.sap.ai.sdk.orchestration.model.EmbeddingsPostRequest; -import com.sap.ai.sdk.orchestration.model.EmbeddingsPostResponse; -import com.sap.ai.sdk.orchestration.model.EmbeddingsResponse; import com.sap.ai.sdk.orchestration.model.ErrorResponse; import com.sap.ai.sdk.orchestration.model.GenericModuleResult; import com.sap.ai.sdk.orchestration.model.GroundingFilterSearchConfiguration; @@ -66,7 +52,6 @@ import com.sap.ai.sdk.orchestration.model.GroundingModuleConfigConfigPlaceholders; import com.sap.ai.sdk.orchestration.model.KeyValueListPair; import com.sap.ai.sdk.orchestration.model.LlamaGuard38b; -import com.sap.ai.sdk.orchestration.model.MaskingModuleConfigProviders; import com.sap.ai.sdk.orchestration.model.ModuleResultsStreaming; import com.sap.ai.sdk.orchestration.model.ResponseFormatText; import com.sap.ai.sdk.orchestration.model.SearchDocumentKeyValueListPair; @@ -79,7 +64,6 @@ import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination; import java.io.IOException; import java.io.InputStream; -import java.math.BigDecimal; import java.nio.file.Files; import java.nio.file.Path; import java.util.List; @@ -1279,154 +1263,4 @@ void testGetAllMessages() { assertThat(messageListTools.get(1)).isInstanceOf(AssistantMessage.class); assertThat(messageListTools.get(2)).isInstanceOf(ToolMessage.class); } - - @Test - void testEmbeddingCallWithMasking() { - - stubFor( - post(urlEqualTo("/v2/embeddings")) - .willReturn( - aResponse() - .withStatus(200) - .withBody( - """ - { - "request_id": "2ee98443-e1ee-9503-b800-e38b5b80fe45", - "intermediate_results": { - "input_masking": { - "message": "Embedding input is masked successfully.", - "data": { - "masked_input": "['Hello', 'MASKED_PERSON', '!']" - } - } - }, - "final_result": { - "object": "list", - "data": [ - { - "object": "embedding", - "embedding": [ - 0.43988228, - -0.82985526, - -0.15936942, - 0.041005015, - 0.30127057 - ], - "index": 0 - } - ], - "model": "text-embedding-3-large", - "usage": { - "prompt_tokens": 10, - "total_tokens": 10 - } - } - } - """))); - - val dpiConfig = - DPIConfig.create() - .type(DPIConfig.TypeEnum.SAP_DATA_PRIVACY_INTEGRATION) - .method(DPIConfig.MethodEnum.ANONYMIZATION) - .entities(List.of(DPIStandardEntity.create().type(DPIEntities.PERSON))); - val maskingConfig = MaskingModuleConfigProviders.create().providers(List.of(dpiConfig)); - - val modelParams = - EmbeddingsModelParams.create() - .encodingFormat(EmbeddingsModelParams.EncodingFormatEnum.FLOAT) - .dimensions(5) - .normalize(false); - val modelConfig = - EmbeddingsModelConfig.create() - .model( - EmbeddingsModelDetails.create().name("text-embedding-3-large").params(modelParams)); - - val orchestrationConfig = - EmbeddingsOrchestrationConfig.create() - .modules( - EmbeddingsModuleConfigs.create().embeddings(modelConfig).masking(maskingConfig)); - - val inputText = - EmbeddingsInput.create().text(EmbeddingsInputText.create("['Hello', 'Müller', '!']")); - - val request = EmbeddingsPostRequest.create().config(orchestrationConfig).input(inputText); - - EmbeddingsPostResponse response = client.embed(request); - - assertThat(response).isNotNull(); - assertThat(response.getRequestId()).isEqualTo("2ee98443-e1ee-9503-b800-e38b5b80fe45"); - - val orchestrationResult = response.getFinalResult(); - assertThat(orchestrationResult).isNotNull(); - assertThat(orchestrationResult.getObject()).isEqualTo(EmbeddingsResponse.ObjectEnum.LIST); - assertThat(orchestrationResult.getModel()).isEqualTo("text-embedding-3-large"); - - val data = orchestrationResult.getData(); - assertThat(data).isNotEmpty(); - assertThat(data.get(0).getEmbedding()) - .isEqualTo( - Embedding.create( - List.of( - BigDecimal.valueOf(0.43988228), - BigDecimal.valueOf(-0.82985526), - BigDecimal.valueOf(-0.15936942), - BigDecimal.valueOf(0.041005015), - BigDecimal.valueOf(0.30127057)))); - assertThat(data.get(0).getIndex()).isZero(); - - val usage = orchestrationResult.getUsage(); - assertThat(usage).isNotNull(); - assertThat(usage.getPromptTokens()).isEqualTo(10); - assertThat(usage.getTotalTokens()).isEqualTo(10); - - val moduleResults = response.getIntermediateResults(); - assertThat(moduleResults).isNotNull(); - assertThat(moduleResults.getInputMasking()).isNotNull(); - assertThat(moduleResults.getInputMasking().getMessage()) - .isEqualTo("Embedding input is masked successfully."); - assertThat(moduleResults.getInputMasking().getData()).isNotNull(); - assertThat(moduleResults.getInputMasking().getData()) - .isEqualTo(Map.of("masked_input", "['Hello', 'MASKED_PERSON', '!']")); - - verify( - postRequestedFor(urlEqualTo("/v2/embeddings")) - .withRequestBody( - equalToJson( - """ - { - "config": { - "modules": { - "embeddings": { - "model": { - "name": "text-embedding-3-large", - "version": "latest", - "params": { - "encoding_format": "float", - "dimensions": 5, - "normalize": false - } - } - }, - "masking": { - "providers": [ - { - "type": "sap_data_privacy_integration", - "method": "anonymization", - "entities": [ - { - "type": "profile-person" - } - ], - "allowlist" : [ ] - } - ] - } - } - }, - "input": { - "text": "['Hello', 'Müller', '!']" - } - } - """))); - } } From f9384fa3f3820675c3d37a5236f8389035351964 Mon Sep 17 00:00:00 2001 From: Roshin Rajan Panackal Date: Thu, 4 Sep 2025 16:17:06 +0200 Subject: [PATCH 5/9] spec update +maxRetries and +timeout --- orchestration/src/test/resources/embeddingRequest.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/orchestration/src/test/resources/embeddingRequest.json b/orchestration/src/test/resources/embeddingRequest.json index 92b35281e..9c201b87e 100644 --- a/orchestration/src/test/resources/embeddingRequest.json +++ b/orchestration/src/test/resources/embeddingRequest.json @@ -6,7 +6,9 @@ "name": "text-embedding-3-small", "params": { "encoding_format": "float" - } + }, + "timeout": 600, + "max_retries": 2 } }, "masking": { From 8e0cd533fd1db00af279c97991f0538b053e962a Mon Sep 17 00:00:00 2001 From: Roshin Rajan Panackal Date: Thu, 4 Sep 2025 16:31:45 +0200 Subject: [PATCH 6/9] model name correction --- .../sap/ai/sdk/orchestration/OrchestrationEmbeddingModel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingModel.java b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingModel.java index 8ef02e93c..09969aa17 100644 --- a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingModel.java +++ b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingModel.java @@ -46,7 +46,7 @@ public class OrchestrationEmbeddingModel implements AiModel { /** Amazon Titan Embed Text model */ public static final OrchestrationEmbeddingModel AMAZON_TITAN_EMBED_TEXT = - new OrchestrationEmbeddingModel("amazon.titan-embed-text"); + new OrchestrationEmbeddingModel("amazon--titan-embed-text"); /** NVIDIA LLaMA 3.2 7B NV EmbedQA model */ public static final OrchestrationEmbeddingModel NVIDIA_LLAMA_32_NV_EMBEDQA_1B = From 3cc558169918b3aa49a6c5c386dd4732c13fc68c Mon Sep 17 00:00:00 2001 From: Roshin Rajan Panackal Date: Wed, 17 Sep 2025 15:05:20 +0200 Subject: [PATCH 7/9] Adjust since versions --- .../com/sap/ai/sdk/orchestration/OrchestrationClient.java | 4 ++-- .../sap/ai/sdk/orchestration/OrchestrationEmbeddingModel.java | 2 +- .../ai/sdk/orchestration/OrchestrationEmbeddingRequest.java | 2 +- .../ai/sdk/orchestration/OrchestrationEmbeddingResponse.java | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationClient.java b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationClient.java index c7b621540..527808881 100644 --- a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationClient.java +++ b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationClient.java @@ -230,7 +230,7 @@ public Stream streamChatCompletionDeltas( * @param request the request containing the input text and other parameters. * @return the response containing the embeddings. * @throws OrchestrationClientException if the request fails - * @since 1.11.0 + * @since 1.12.0 */ @Nonnull public OrchestrationEmbeddingResponse embed(@Nonnull final OrchestrationEmbeddingRequest request) @@ -248,7 +248,7 @@ public OrchestrationEmbeddingResponse embed(@Nonnull final OrchestrationEmbeddin * @param request the low-level API request * @return the low level response object * @throws OrchestrationClientException if the request fails - * @since 1.11.0 + * @since 1.12.0 * @see #embed(OrchestrationEmbeddingRequest) */ @Nonnull diff --git a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingModel.java b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingModel.java index 09969aa17..d9d8b8b46 100644 --- a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingModel.java +++ b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingModel.java @@ -16,7 +16,7 @@ /** * Configuration for embedding models in the Orchestration service. * - * @since 1.11.0 + * @since 1.12.0 */ @Beta @With diff --git a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingRequest.java b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingRequest.java index 9367618c3..9a4df5458 100644 --- a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingRequest.java +++ b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingRequest.java @@ -24,7 +24,7 @@ /** * Represents a request for generating embeddings through the SAP AI Core Orchestration service. * - * @since 1.11.0 + * @since 1.12.0 */ @Beta @Value diff --git a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingResponse.java b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingResponse.java index e6fd16ff7..109825775 100644 --- a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingResponse.java +++ b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingResponse.java @@ -16,7 +16,7 @@ * *

Wraps {@link EmbeddingsPostResponse} and provides convenient access to embedding vectors. * - * @since 1.11.0 + * @since 1.12.0 */ @Beta @Value From 2b6f8fe9a6896a9e5a9bcf3ace2d852ba4a8674a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20D=C3=BCmont?= Date: Mon, 22 Sep 2025 16:41:41 +0200 Subject: [PATCH 8/9] Fix minor naming issues --- .../OrchestrationEmbeddingRequest.java | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingRequest.java b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingRequest.java index 9a4df5458..f70e2e670 100644 --- a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingRequest.java +++ b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingRequest.java @@ -35,18 +35,18 @@ public class OrchestrationEmbeddingRequest { @Nonnull OrchestrationEmbeddingModel model; /** The list of text inputs to be converted into embeddings. */ - @Nonnull List tokens; + @Nonnull List inputs; /** Optional masking providers for data privacy and security. */ @With(value = PRIVATE) @Nullable List masking; - /** Optional token type classification to optimize embedding generation. */ + /** Optional embedding input type classification to optimize embedding generation. */ @With(value = PRIVATE) @Getter(NONE) @Nullable - EmbeddingsInput.TypeEnum tokenType; + EmbeddingsInput.TypeEnum inputType; /** * Create an embedding request using fluent API starting with model selection. @@ -60,7 +60,7 @@ public class OrchestrationEmbeddingRequest { */ @Nonnull public static InputStep forModel(@Nonnull final OrchestrationEmbeddingModel model) { - return tokens -> new OrchestrationEmbeddingRequest(model, List.copyOf(tokens), null, null); + return inputs -> new OrchestrationEmbeddingRequest(model, List.copyOf(inputs), null, null); } /** Builder step for specifying text inputs to embed. */ @@ -70,21 +70,22 @@ public interface InputStep { /** * Specifies text inputs to be embedded. * - * @param tokens the text strings to embed + * @param inputs the text strings to embed * @return a new embedding request instance */ @Nonnull - OrchestrationEmbeddingRequest forInputs(@Nonnull final List tokens); + OrchestrationEmbeddingRequest forInputs(@Nonnull final List inputs); /** * Specifies multiple text inputs using variable arguments. * - * @param tokens one or more strings to embed + * @param inputs one or more strings to embed * @return a new embedding request instance */ @Nonnull - default OrchestrationEmbeddingRequest forInputs(@Nonnull final String... tokens) { - return forInputs(List.of(tokens)); + default OrchestrationEmbeddingRequest forInputs( + @Nonnull final String firstInput, String... inputs) { + return forInputs(Lists.asList(firstInput, inputs)); } } @@ -111,7 +112,7 @@ public OrchestrationEmbeddingRequest withMasking( */ @Nonnull public OrchestrationEmbeddingRequest asDocument() { - return withTokenType(EmbeddingsInput.TypeEnum.DOCUMENT); + return withInputType(EmbeddingsInput.TypeEnum.DOCUMENT); } /** @@ -121,7 +122,7 @@ public OrchestrationEmbeddingRequest asDocument() { */ @Nonnull public OrchestrationEmbeddingRequest asText() { - return withTokenType(EmbeddingsInput.TypeEnum.TEXT); + return withInputType(EmbeddingsInput.TypeEnum.TEXT); } /** @@ -131,14 +132,14 @@ public OrchestrationEmbeddingRequest asText() { */ @Nonnull public OrchestrationEmbeddingRequest asQuery() { - return withTokenType(EmbeddingsInput.TypeEnum.QUERY); + return withInputType(EmbeddingsInput.TypeEnum.QUERY); } @Nonnull EmbeddingsPostRequest createEmbeddingsPostRequest() { final var input = - EmbeddingsInput.create().text(EmbeddingsInputText.create(tokens)).type(tokenType); + EmbeddingsInput.create().text(EmbeddingsInputText.create(inputs)).type(inputType); final var embeddingsModelConfig = EmbeddingsModelConfig.create().model(model.createEmbeddingsModelDetails()); final var modules = From 6e16516e038d2b830670447eebda2688ecad44c5 Mon Sep 17 00:00:00 2001 From: Jonas Israel Date: Thu, 2 Oct 2025 13:25:58 +0200 Subject: [PATCH 9/9] mini fixes --- .../ai/sdk/orchestration/OrchestrationEmbeddingRequest.java | 5 +++-- .../com/sap/ai/sdk/app/services/OrchestrationService.java | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingRequest.java b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingRequest.java index f70e2e670..d1ebfe91e 100644 --- a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingRequest.java +++ b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationEmbeddingRequest.java @@ -79,12 +79,13 @@ public interface InputStep { /** * Specifies multiple text inputs using variable arguments. * - * @param inputs one or more strings to embed + * @param firstInput string to embed + * @param inputs optional additional strings to embed * @return a new embedding request instance */ @Nonnull default OrchestrationEmbeddingRequest forInputs( - @Nonnull final String firstInput, String... inputs) { + @Nonnull final String firstInput, @Nonnull final String... inputs) { return forInputs(Lists.asList(firstInput, inputs)); } } diff --git a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/OrchestrationService.java b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/OrchestrationService.java index c6dd28c27..d74a55bba 100644 --- a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/OrchestrationService.java +++ b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/OrchestrationService.java @@ -4,7 +4,6 @@ import static com.sap.ai.sdk.orchestration.OrchestrationAiModel.GPT_4O_MINI; import static com.sap.ai.sdk.orchestration.OrchestrationAiModel.Parameter.TEMPERATURE; import static com.sap.ai.sdk.orchestration.OrchestrationEmbeddingModel.TEXT_EMBEDDING_3_SMALL; -import static com.sap.ai.sdk.orchestration.model.SAPDocumentTranslation.TypeEnum.SAP_DOCUMENT_TRANSLATION; import com.fasterxml.jackson.annotation.JsonProperty; import com.sap.ai.sdk.core.AiCoreService;