From 9614895fb46fd8e2533c02158dceb04b346929e5 Mon Sep 17 00:00:00 2001 From: Nourhan Shata Date: Wed, 25 Feb 2026 10:48:25 +0100 Subject: [PATCH 01/14] full implementation --- .../TranslationApplyToSelector.java | 115 ++++++++++++++++++ .../sdk/orchestration/TranslationConfig.java | 18 ++- .../OrchestrationModuleConfigTest.java | 40 ++++++ .../app/services/OrchestrationService.java | 31 +++-- .../app/controllers/OrchestrationTest.java | 19 +-- 5 files changed, 201 insertions(+), 22 deletions(-) create mode 100644 orchestration/src/main/java/com/sap/ai/sdk/orchestration/TranslationApplyToSelector.java diff --git a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/TranslationApplyToSelector.java b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/TranslationApplyToSelector.java new file mode 100644 index 000000000..bbec39983 --- /dev/null +++ b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/TranslationApplyToSelector.java @@ -0,0 +1,115 @@ +package com.sap.ai.sdk.orchestration; + +import static com.sap.ai.sdk.orchestration.model.SAPDocumentTranslationApplyToSelector.CategoryEnum.PLACEHOLDERS; +import static com.sap.ai.sdk.orchestration.model.SAPDocumentTranslationApplyToSelector.CategoryEnum.TEMPLATE_ROLES; + +import com.sap.ai.sdk.orchestration.model.SAPDocumentTranslationApplyToSelector; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import javax.annotation.Nonnull; + +/** + * Convenience builder for {@link SAPDocumentTranslationApplyToSelector}. + * + *

This avoids passing raw strings for template roles and keeps sample-code readable. + */ +public final class TranslationApplyToSelector { + private TranslationApplyToSelector() {} + + /** + * Supported values for {@code items[]} when {@code category=template_roles}. + * + *

These map to the roles used in prompt templates. + */ + public enum TemplateRole { + /** Template role for user messages. */ + USER("user"), + + /** Template role for system messages. */ + SYSTEM("system"), + + /** Template role for assistant messages. */ + ASSISTANT("assistant"), + + /** Template role for developer messages. */ + DEVELOPER("developer"), + + /** Template role for tool messages. */ + TOOL("tool"); + + private final String value; + + TemplateRole(@Nonnull final String value) { + this.value = value; + } + + /** + * Get the string representation used in the API payload. + * + * @return The role value used in {@code items[]}. + */ + @Nonnull + public String getValue() { + return value; + } + } + + /** + * Start an {@code apply_to} selector for placeholder names in {@code placeholder_values}. + * + * @param names The placeholder keys to translate. + * @return A selector with {@code category=placeholders} and the given items. + */ + @Nonnull + public static SAPDocumentTranslationApplyToSelector placeholders(@Nonnull final String... names) { + Objects.requireNonNull(names, "names must not be null"); + return placeholders(List.of(names)); + } + + /** + * Start an {@code apply_to} selector for placeholder names in {@code placeholder_values}. + * + * @param names The placeholder keys to translate. + * @return A selector with {@code category=placeholders} and the given items. + */ + @Nonnull + public static SAPDocumentTranslationApplyToSelector placeholders( + @Nonnull final List names) { + Objects.requireNonNull(names, "names must not be null"); + return SAPDocumentTranslationApplyToSelector.create() + .category(PLACEHOLDERS) + .items(List.copyOf(names)); + } + + /** + * Start an {@code apply_to} selector for prompt template message roles. + * + * @param roles The template roles to translate. + * @return A selector with {@code category=template_roles} and the given items. + */ + @Nonnull + public static SAPDocumentTranslationApplyToSelector templateRoles( + @Nonnull final TemplateRole... roles) { + Objects.requireNonNull(roles, "roles must not be null"); + return templateRoles(List.of(roles)); + } + + /** + * Start an {@code apply_to} selector for prompt template message roles. + * + * @param roles The template roles to translate. + * @return A selector with {@code category=template_roles} and the given items. + */ + @Nonnull + public static SAPDocumentTranslationApplyToSelector templateRoles( + @Nonnull final Collection roles) { + Objects.requireNonNull(roles, "roles must not be null"); + final var roleStrings = + roles.stream().filter(Objects::nonNull).map(TemplateRole::getValue).toList(); + + return SAPDocumentTranslationApplyToSelector.create() + .category(TEMPLATE_ROLES) + .items(roleStrings); + } +} diff --git a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/TranslationConfig.java b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/TranslationConfig.java index 6210f6f7a..f830bd895 100644 --- a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/TranslationConfig.java +++ b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/TranslationConfig.java @@ -1,10 +1,12 @@ package com.sap.ai.sdk.orchestration; +import com.sap.ai.sdk.orchestration.model.SAPDocumentTranslationApplyToSelector; import com.sap.ai.sdk.orchestration.model.SAPDocumentTranslationInput; import com.sap.ai.sdk.orchestration.model.SAPDocumentTranslationInputConfig; import com.sap.ai.sdk.orchestration.model.SAPDocumentTranslationOutput; import com.sap.ai.sdk.orchestration.model.SAPDocumentTranslationOutputConfig; import com.sap.ai.sdk.orchestration.model.SAPDocumentTranslationOutputTargetLanguage; +import java.util.List; import javax.annotation.Nonnull; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; @@ -29,13 +31,21 @@ class Input implements TranslationConfig { @With String sourceLanguage; - Object ApplyTo; // Can be null + /** + * Optional selection(s) to translate. If empty or null, translation is applied to the whole + * user input. + */ + @With List applyTo; @Nonnull SAPDocumentTranslationInput createSAPDocumentTranslationInput() { val translationType = SAPDocumentTranslationInput.TypeEnum.SAP_DOCUMENT_TRANSLATION; - val conf = - SAPDocumentTranslationInputConfig.create().targetLanguage(targetLanguage).applyTo(null); + final var conf = SAPDocumentTranslationInputConfig.create().targetLanguage(targetLanguage); + + if (applyTo != null && !applyTo.isEmpty()) { + conf.applyTo(applyTo); + } + return SAPDocumentTranslationInput.create().type(translationType).config(conf); } } @@ -71,7 +81,6 @@ SAPDocumentTranslationOutput createSAPDocumentTranslationOutput() { */ @Nonnull static TranslationConfig.Input translateInputTo(@Nonnull final String targetLanguage) { - return new TranslationConfig.Input(targetLanguage, null, null); } @@ -86,7 +95,6 @@ static TranslationConfig.Input translateInputTo(@Nonnull final String targetLang */ @Nonnull static TranslationConfig.Output translateOutputTo(@Nonnull final String targetLanguage) { - return new TranslationConfig.Output(targetLanguage, null); } } diff --git a/orchestration/src/test/java/com/sap/ai/sdk/orchestration/OrchestrationModuleConfigTest.java b/orchestration/src/test/java/com/sap/ai/sdk/orchestration/OrchestrationModuleConfigTest.java index fcbeaed33..a47bd9e02 100644 --- a/orchestration/src/test/java/com/sap/ai/sdk/orchestration/OrchestrationModuleConfigTest.java +++ b/orchestration/src/test/java/com/sap/ai/sdk/orchestration/OrchestrationModuleConfigTest.java @@ -175,6 +175,46 @@ void testTranslationConfig() { .getSourceLanguage()); } + @Test + void testTranslationConfigApplyToSelectors() { + var selector = + TranslationApplyToSelector.placeholders("exam_type", "topic").sourceLanguage("de-DE"); + + final var inputTranslationConfig = + TranslationConfig.translateInputTo("en-US").withApplyTo(List.of(selector)); + + final var sapInput = inputTranslationConfig.createSAPDocumentTranslationInput(); + assertThat(sapInput.getConfig().getTargetLanguage()).isEqualTo("en-US"); + assertThat(sapInput.getConfig().getApplyTo()).hasSize(1); + assertThat(sapInput.getConfig().getApplyTo().get(0).getCategory().getValue()) + .isEqualTo("placeholders"); + assertThat(sapInput.getConfig().getApplyTo().get(0).getItems()) + .containsExactly("exam_type", "topic"); + + final var inputNull = TranslationConfig.translateInputTo("en-US"); + final var sapNull = inputNull.createSAPDocumentTranslationInput(); + assertThat(sapNull.getConfig().getApplyTo()).isEmpty(); + + // applyTo == empty list + final var inputEmpty = TranslationConfig.translateInputTo("en-US").withApplyTo(List.of()); + final var sapEmpty = inputEmpty.createSAPDocumentTranslationInput(); + assertThat(sapEmpty.getConfig().getApplyTo()).isEmpty(); + + selector = + TranslationApplyToSelector.templateRoles( + TranslationApplyToSelector.TemplateRole.USER, + TranslationApplyToSelector.TemplateRole.SYSTEM, + TranslationApplyToSelector.TemplateRole.ASSISTANT, + TranslationApplyToSelector.TemplateRole.DEVELOPER, + TranslationApplyToSelector.TemplateRole.TOOL) + .sourceLanguage("de-DE"); + + assertThat(selector.getCategory().getValue()).isEqualTo("template_roles"); + assertThat(selector.getItems()) + .containsExactly("user", "system", "assistant", "developer", "tool"); + assertThat(selector.getSourceLanguage()).isEqualTo("de-DE"); + } + @Test void testParams() { // test withParams(Map) 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 9c7890feb..e078cdacf 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 @@ -28,6 +28,7 @@ import com.sap.ai.sdk.orchestration.ResponseJsonSchema; import com.sap.ai.sdk.orchestration.SystemMessage; import com.sap.ai.sdk.orchestration.TemplateConfig; +import com.sap.ai.sdk.orchestration.TranslationApplyToSelector; import com.sap.ai.sdk.orchestration.TranslationConfig; import com.sap.ai.sdk.orchestration.model.DPIEntities; import com.sap.ai.sdk.orchestration.model.DataRepositoryType; @@ -681,17 +682,31 @@ public OrchestrationChatResponse localPromptTemplate(@Nonnull final String promp */ @Nonnull public OrchestrationChatResponse translation() { - val prompt = - new OrchestrationPrompt( - "Quelle est la couleur de la tour Eiffel? Et en quelle langue tu me parles maintenant?"); - // list of supported language pairs - // https://help.sap.com/docs/translation-hub/sap-translation-hub/supported-languages?version=Cloud#translation-provider-sap-machine-translation + val inputParams = + Map.of("exam_type", "Abitur", "topic", "Deutsche Literatur", "num_questions", "5"); + + val systemMessage = + Message.system( + "You are an expert study coach creating clear, concise exam notes and practice questions."); + val userMessage = + Message.user( + "Generate a study guide for the {{?exam_type}} exam on {{?topic}}.\n\nInclude {{?num_questions}} practice questions."); + val templatingConfig = TemplateConfig.create().withMessages(systemMessage, userMessage); + + val prompt = new OrchestrationPrompt(inputParams); + val configWithTranslation = config - .withInputTranslationConfig(TranslationConfig.translateInputTo("en-US")) + .withTemplateConfig(templatingConfig) + .withInputTranslationConfig( + TranslationConfig.translateInputTo("en-US") + .withApplyTo( + List.of( + // Translate only selected placeholder values from German to English + TranslationApplyToSelector.placeholders(List.of("exam_type", "topic")) + .sourceLanguage("de-DE")))) .withOutputTranslationConfig( - TranslationConfig.translateOutputTo("de-DE") - .withSourceLanguage("en-US")); // optional source language + TranslationConfig.translateOutputTo("de-DE").withSourceLanguage("en-US")); return client.chatCompletion(prompt, configWithTranslation); } 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 5051e091c..ca7906b3d 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 @@ -22,7 +22,6 @@ import com.sap.ai.sdk.orchestration.TemplateConfig; import com.sap.ai.sdk.orchestration.TextItem; import com.sap.ai.sdk.orchestration.model.DPIEntities; -import com.sap.ai.sdk.orchestration.model.GenericModuleResult; import com.sap.ai.sdk.orchestration.model.InputTranslationModuleResult; import java.io.IOException; import java.io.InputStream; @@ -225,7 +224,7 @@ void testGroundingSharepoint() { assertThat(response).isNotNull(); var result = response.getOriginalResponse(); var llmChoice = result.getFinalResult().getChoices().get(0); - assertThat(llmChoice.getMessage().getContent()).contains("&)UPnkL_izT)&1u%?2Kg*Y.@qFqR@/"); + assertThat(llmChoice.getMessage().getContent()).contains("&)UPNkL_izT)&1u%?2Kg*Y.@qFqR@/"); } @Test @@ -496,18 +495,20 @@ void testStreamingErrorHandlingMasking() { void testTranslation() { val result = service.translation(); val content = result.getContent(); - // English translated to German - assertThat(content).contains("Englisch"); - assertThat(content).contains("Der", "ist"); + // Output translation turns the model response back to German + assertThat(content) + .containsAnyOf("Abitur", "Deutsche", "Literatur", "Lern", "Übungs", "Fragen"); InputTranslationModuleResult inputTranslation = result.getOriginalResponse().getIntermediateResults().getInputTranslation(); - GenericModuleResult outputTranslation = - result.getOriginalResponse().getIntermediateResults().getOutputTranslation(); assertThat(inputTranslation).isNotNull(); - assertThat(outputTranslation).isNotNull(); assertThat(inputTranslation.getMessage()) - .isEqualTo("Translated messages with roles: ['user']. "); + .isNotNull() + .contains("Successfully translated placeholders: ['exam_type', 'topic']. "); + + val outputTranslation = + result.getOriginalResponse().getIntermediateResults().getOutputTranslation(); + assertThat(outputTranslation).isNotNull(); assertThat(outputTranslation.getMessage()).isEqualTo("Output Translation successful"); } From 3aec367cf6fa7a1a900e911135c106dfa288c8ab Mon Sep 17 00:00:00 2001 From: Nourhan Shata Date: Wed, 25 Feb 2026 11:06:35 +0100 Subject: [PATCH 02/14] release notes --- docs/release_notes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/release_notes.md b/docs/release_notes.md index 80e2325c3..b9af822c7 100644 --- a/docs/release_notes.md +++ b/docs/release_notes.md @@ -16,7 +16,7 @@ ### 📈 Improvements -- +- [Orchestration] Added new API `TranslationConfig#withApplyTo` to support partial translation for user's input. ### 🐛 Fixed Issues From a429f3780a49c2f1136eb1b53573edeb3014e5a9 Mon Sep 17 00:00:00 2001 From: Nourhan Islam Shata <163640161+n-o-u-r-h-a-n@users.noreply.github.com> Date: Wed, 25 Feb 2026 11:58:51 +0100 Subject: [PATCH 03/14] Update sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/OrchestrationService.java Co-authored-by: Charles Dubois <103174266+CharlesDuboisSAP@users.noreply.github.com> --- .../java/com/sap/ai/sdk/app/services/OrchestrationService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 895e970e2..fb6c62fb6 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 @@ -711,7 +711,7 @@ public OrchestrationChatResponse translation() { List.of( // Translate only selected placeholder values from German to English TranslationApplyToSelector.placeholders(List.of("exam_type", "topic")) - .sourceLanguage("de-DE")))) + )).withSourceLanguage("de-DE")) .withOutputTranslationConfig( TranslationConfig.translateOutputTo("de-DE").withSourceLanguage("en-US")); From 46230db51252358ba407d43d70feccc10e5b6872 Mon Sep 17 00:00:00 2001 From: SAP Cloud SDK Bot Date: Wed, 25 Feb 2026 10:59:41 +0000 Subject: [PATCH 04/14] Formatting --- .../com/sap/ai/sdk/app/services/OrchestrationService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 fb6c62fb6..defb1599e 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 @@ -710,8 +710,8 @@ public OrchestrationChatResponse translation() { .withApplyTo( List.of( // Translate only selected placeholder values from German to English - TranslationApplyToSelector.placeholders(List.of("exam_type", "topic")) - )).withSourceLanguage("de-DE")) + TranslationApplyToSelector.placeholders(List.of("exam_type", "topic")))) + .withSourceLanguage("de-DE")) .withOutputTranslationConfig( TranslationConfig.translateOutputTo("de-DE").withSourceLanguage("en-US")); From fdf7a5e00477d5492be756d45103191ffd7fb679 Mon Sep 17 00:00:00 2001 From: Nourhan Shata Date: Wed, 25 Feb 2026 12:07:20 +0100 Subject: [PATCH 05/14] reverting --- .../com/sap/ai/sdk/app/controllers/OrchestrationTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 d3a9b8f73..7762c7239 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 @@ -224,7 +224,7 @@ void testGroundingSharepoint() { assertThat(response).isNotNull(); var result = response.getOriginalResponse(); var llmChoice = result.getFinalResult().getChoices().get(0); - assertThat(llmChoice.getMessage().getContent()).contains("&)UPNkL_izT)&1u%?2Kg*Y.@qFqR@/"); + assertThat(llmChoice.getMessage().getContent()).contains("&)UPnkL_izT)&1u%?2Kg*Y.@qFqR@/"); } @Test @@ -505,7 +505,7 @@ void testTranslation() { assertThat(inputTranslation).isNotNull(); assertThat(inputTranslation.getMessage()) .isNotNull() - .contains("Successfully translated placeholders: ['exam_type', 'topic']. "); + .contains("Successfully translated placeholders: ['exam_type', 'topic']. "); val outputTranslation = result.getOriginalResponse().getIntermediateResults().getOutputTranslation(); From 5b8e5367722409fd63df7670c90da4bb0c119d37 Mon Sep 17 00:00:00 2001 From: SAP Cloud SDK Bot Date: Wed, 25 Feb 2026 11:08:19 +0000 Subject: [PATCH 06/14] Formatting --- .../java/com/sap/ai/sdk/app/controllers/OrchestrationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 7762c7239..c17d0ff20 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 @@ -505,7 +505,7 @@ void testTranslation() { assertThat(inputTranslation).isNotNull(); assertThat(inputTranslation.getMessage()) .isNotNull() - .contains("Successfully translated placeholders: ['exam_type', 'topic']. "); + .contains("Successfully translated placeholders: ['exam_type', 'topic']. "); val outputTranslation = result.getOriginalResponse().getIntermediateResults().getOutputTranslation(); From 9a5a2002322b074abe171018c4922b1c80d4c0b0 Mon Sep 17 00:00:00 2001 From: Nourhan Shata Date: Wed, 25 Feb 2026 12:16:08 +0100 Subject: [PATCH 07/14] refactoring the new class name --- ...ranslationApplyToSelector.java => ApplyTo.java} | 4 ++-- .../OrchestrationModuleConfigTest.java | 14 +++++++------- .../ai/sdk/app/services/OrchestrationService.java | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) rename orchestration/src/main/java/com/sap/ai/sdk/orchestration/{TranslationApplyToSelector.java => ApplyTo.java} (97%) diff --git a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/TranslationApplyToSelector.java b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/ApplyTo.java similarity index 97% rename from orchestration/src/main/java/com/sap/ai/sdk/orchestration/TranslationApplyToSelector.java rename to orchestration/src/main/java/com/sap/ai/sdk/orchestration/ApplyTo.java index bbec39983..9c956195c 100644 --- a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/TranslationApplyToSelector.java +++ b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/ApplyTo.java @@ -14,8 +14,8 @@ * *

This avoids passing raw strings for template roles and keeps sample-code readable. */ -public final class TranslationApplyToSelector { - private TranslationApplyToSelector() {} +public final class ApplyTo { + private ApplyTo() {} /** * Supported values for {@code items[]} when {@code category=template_roles}. diff --git a/orchestration/src/test/java/com/sap/ai/sdk/orchestration/OrchestrationModuleConfigTest.java b/orchestration/src/test/java/com/sap/ai/sdk/orchestration/OrchestrationModuleConfigTest.java index 995e6a3a8..ae6803114 100644 --- a/orchestration/src/test/java/com/sap/ai/sdk/orchestration/OrchestrationModuleConfigTest.java +++ b/orchestration/src/test/java/com/sap/ai/sdk/orchestration/OrchestrationModuleConfigTest.java @@ -179,7 +179,7 @@ void testTranslationConfig() { @Test void testTranslationConfigApplyToSelectors() { var selector = - TranslationApplyToSelector.placeholders("exam_type", "topic").sourceLanguage("de-DE"); + ApplyTo.placeholders("exam_type", "topic").sourceLanguage("de-DE"); final var inputTranslationConfig = TranslationConfig.translateInputTo("en-US").withApplyTo(List.of(selector)); @@ -202,12 +202,12 @@ void testTranslationConfigApplyToSelectors() { assertThat(sapEmpty.getConfig().getApplyTo()).isEmpty(); selector = - TranslationApplyToSelector.templateRoles( - TranslationApplyToSelector.TemplateRole.USER, - TranslationApplyToSelector.TemplateRole.SYSTEM, - TranslationApplyToSelector.TemplateRole.ASSISTANT, - TranslationApplyToSelector.TemplateRole.DEVELOPER, - TranslationApplyToSelector.TemplateRole.TOOL) + ApplyTo.templateRoles( + ApplyTo.TemplateRole.USER, + ApplyTo.TemplateRole.SYSTEM, + ApplyTo.TemplateRole.ASSISTANT, + ApplyTo.TemplateRole.DEVELOPER, + ApplyTo.TemplateRole.TOOL) .sourceLanguage("de-DE"); assertThat(selector.getCategory().getValue()).isEqualTo("template_roles"); 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 defb1599e..330a4533f 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 @@ -28,7 +28,7 @@ import com.sap.ai.sdk.orchestration.ResponseJsonSchema; import com.sap.ai.sdk.orchestration.SystemMessage; import com.sap.ai.sdk.orchestration.TemplateConfig; -import com.sap.ai.sdk.orchestration.TranslationApplyToSelector; +import com.sap.ai.sdk.orchestration.ApplyTo; import com.sap.ai.sdk.orchestration.TranslationConfig; import com.sap.ai.sdk.orchestration.model.DPIEntities; import com.sap.ai.sdk.orchestration.model.DataRepositoryType; @@ -710,7 +710,7 @@ public OrchestrationChatResponse translation() { .withApplyTo( List.of( // Translate only selected placeholder values from German to English - TranslationApplyToSelector.placeholders(List.of("exam_type", "topic")))) + ApplyTo.placeholders(List.of("exam_type", "topic")))) .withSourceLanguage("de-DE")) .withOutputTranslationConfig( TranslationConfig.translateOutputTo("de-DE").withSourceLanguage("en-US")); From d3cf79d7db436e2a059a7889752801be4e7021ce Mon Sep 17 00:00:00 2001 From: SAP Cloud SDK Bot Date: Wed, 25 Feb 2026 11:16:57 +0000 Subject: [PATCH 08/14] Formatting --- .../ai/sdk/orchestration/OrchestrationModuleConfigTest.java | 3 +-- .../java/com/sap/ai/sdk/app/services/OrchestrationService.java | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/orchestration/src/test/java/com/sap/ai/sdk/orchestration/OrchestrationModuleConfigTest.java b/orchestration/src/test/java/com/sap/ai/sdk/orchestration/OrchestrationModuleConfigTest.java index ae6803114..4e21555fc 100644 --- a/orchestration/src/test/java/com/sap/ai/sdk/orchestration/OrchestrationModuleConfigTest.java +++ b/orchestration/src/test/java/com/sap/ai/sdk/orchestration/OrchestrationModuleConfigTest.java @@ -178,8 +178,7 @@ void testTranslationConfig() { @Test void testTranslationConfigApplyToSelectors() { - var selector = - ApplyTo.placeholders("exam_type", "topic").sourceLanguage("de-DE"); + var selector = ApplyTo.placeholders("exam_type", "topic").sourceLanguage("de-DE"); final var inputTranslationConfig = TranslationConfig.translateInputTo("en-US").withApplyTo(List.of(selector)); 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 330a4533f..4076958ad 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 @@ -9,6 +9,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.sap.ai.sdk.core.AiCoreService; +import com.sap.ai.sdk.orchestration.ApplyTo; import com.sap.ai.sdk.orchestration.AzureContentFilter; import com.sap.ai.sdk.orchestration.AzureFilterThreshold; import com.sap.ai.sdk.orchestration.DpiMasking; @@ -28,7 +29,6 @@ import com.sap.ai.sdk.orchestration.ResponseJsonSchema; import com.sap.ai.sdk.orchestration.SystemMessage; import com.sap.ai.sdk.orchestration.TemplateConfig; -import com.sap.ai.sdk.orchestration.ApplyTo; import com.sap.ai.sdk.orchestration.TranslationConfig; import com.sap.ai.sdk.orchestration.model.DPIEntities; import com.sap.ai.sdk.orchestration.model.DataRepositoryType; From dc21aea8e9069f95c6cc026e5254b144f359991d Mon Sep 17 00:00:00 2001 From: Nourhan Shata Date: Wed, 25 Feb 2026 12:35:18 +0100 Subject: [PATCH 09/14] reverting a comment --- .../java/com/sap/ai/sdk/app/services/OrchestrationService.java | 2 ++ 1 file changed, 2 insertions(+) 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 330a4533f..99f0f67b9 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 @@ -701,6 +701,8 @@ public OrchestrationChatResponse translation() { val templatingConfig = TemplateConfig.create().withMessages(systemMessage, userMessage); val prompt = new OrchestrationPrompt(inputParams); + // list of supported language pairs + // https://help.sap.com/docs/translation-hub/sap-translation-hub/supported-languages?version=Cloud#translation-provider-sap-machine-translation val configWithTranslation = config From 0fb1bc3a8cb434827504083054d63941d505dc49 Mon Sep 17 00:00:00 2001 From: Nourhan Shata Date: Wed, 25 Feb 2026 13:04:25 +0100 Subject: [PATCH 10/14] fixed test + reduced methods --- .../com/sap/ai/sdk/orchestration/ApplyTo.java | 34 +++---------------- .../app/services/OrchestrationService.java | 2 +- .../app/controllers/OrchestrationTest.java | 4 ++- 3 files changed, 8 insertions(+), 32 deletions(-) diff --git a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/ApplyTo.java b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/ApplyTo.java index 9c956195c..05627b1d5 100644 --- a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/ApplyTo.java +++ b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/ApplyTo.java @@ -4,6 +4,8 @@ import static com.sap.ai.sdk.orchestration.model.SAPDocumentTranslationApplyToSelector.CategoryEnum.TEMPLATE_ROLES; import com.sap.ai.sdk.orchestration.model.SAPDocumentTranslationApplyToSelector; +import lombok.Getter; + import java.util.Collection; import java.util.List; import java.util.Objects; @@ -38,21 +40,11 @@ public enum TemplateRole { /** Template role for tool messages. */ TOOL("tool"); - private final String value; + @Getter private final String value; TemplateRole(@Nonnull final String value) { this.value = value; } - - /** - * Get the string representation used in the API payload. - * - * @return The role value used in {@code items[]}. - */ - @Nonnull - public String getValue() { - return value; - } } /** @@ -63,23 +55,7 @@ public String getValue() { */ @Nonnull public static SAPDocumentTranslationApplyToSelector placeholders(@Nonnull final String... names) { - Objects.requireNonNull(names, "names must not be null"); - return placeholders(List.of(names)); - } - - /** - * Start an {@code apply_to} selector for placeholder names in {@code placeholder_values}. - * - * @param names The placeholder keys to translate. - * @return A selector with {@code category=placeholders} and the given items. - */ - @Nonnull - public static SAPDocumentTranslationApplyToSelector placeholders( - @Nonnull final List names) { - Objects.requireNonNull(names, "names must not be null"); - return SAPDocumentTranslationApplyToSelector.create() - .category(PLACEHOLDERS) - .items(List.copyOf(names)); + return SAPDocumentTranslationApplyToSelector.create().category(PLACEHOLDERS).items(names); } /** @@ -91,7 +67,6 @@ public static SAPDocumentTranslationApplyToSelector placeholders( @Nonnull public static SAPDocumentTranslationApplyToSelector templateRoles( @Nonnull final TemplateRole... roles) { - Objects.requireNonNull(roles, "roles must not be null"); return templateRoles(List.of(roles)); } @@ -104,7 +79,6 @@ public static SAPDocumentTranslationApplyToSelector templateRoles( @Nonnull public static SAPDocumentTranslationApplyToSelector templateRoles( @Nonnull final Collection roles) { - Objects.requireNonNull(roles, "roles must not be null"); final var roleStrings = roles.stream().filter(Objects::nonNull).map(TemplateRole::getValue).toList(); 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 b39f9b4b1..fdbe3c326 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 @@ -712,7 +712,7 @@ public OrchestrationChatResponse translation() { .withApplyTo( List.of( // Translate only selected placeholder values from German to English - ApplyTo.placeholders(List.of("exam_type", "topic")))) + ApplyTo.placeholders("exam_type", "topic"))) .withSourceLanguage("de-DE")) .withOutputTranslationConfig( TranslationConfig.translateOutputTo("de-DE").withSourceLanguage("en-US")); 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 c17d0ff20..0b2ada3c4 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 @@ -505,7 +505,9 @@ void testTranslation() { assertThat(inputTranslation).isNotNull(); assertThat(inputTranslation.getMessage()) .isNotNull() - .contains("Successfully translated placeholders: ['exam_type', 'topic']. "); + .contains("Successfully translated placeholders:") + .contains("exam_type") + .contains("topic"); val outputTranslation = result.getOriginalResponse().getIntermediateResults().getOutputTranslation(); From 20b4c27ab92482a07913dcedcdffe28f00b8932b Mon Sep 17 00:00:00 2001 From: SAP Cloud SDK Bot Date: Wed, 25 Feb 2026 12:05:23 +0000 Subject: [PATCH 11/14] Formatting --- .../src/main/java/com/sap/ai/sdk/orchestration/ApplyTo.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/ApplyTo.java b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/ApplyTo.java index 05627b1d5..0be8923b7 100644 --- a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/ApplyTo.java +++ b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/ApplyTo.java @@ -4,12 +4,11 @@ import static com.sap.ai.sdk.orchestration.model.SAPDocumentTranslationApplyToSelector.CategoryEnum.TEMPLATE_ROLES; import com.sap.ai.sdk.orchestration.model.SAPDocumentTranslationApplyToSelector; -import lombok.Getter; - import java.util.Collection; import java.util.List; import java.util.Objects; import javax.annotation.Nonnull; +import lombok.Getter; /** * Convenience builder for {@link SAPDocumentTranslationApplyToSelector}. From 7918494ac3483201798135841e52b959ef8802a6 Mon Sep 17 00:00:00 2001 From: Nourhan Shata Date: Wed, 25 Feb 2026 13:53:20 +0100 Subject: [PATCH 12/14] further method reduction --- .../com/sap/ai/sdk/orchestration/ApplyTo.java | 15 ++------------- .../OrchestrationModuleConfigTest.java | 13 ++++++------- 2 files changed, 8 insertions(+), 20 deletions(-) diff --git a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/ApplyTo.java b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/ApplyTo.java index 05627b1d5..e87bdd8b7 100644 --- a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/ApplyTo.java +++ b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/ApplyTo.java @@ -9,6 +9,7 @@ import java.util.Collection; import java.util.List; import java.util.Objects; +import java.util.stream.Stream; import javax.annotation.Nonnull; /** @@ -67,20 +68,8 @@ public static SAPDocumentTranslationApplyToSelector placeholders(@Nonnull final @Nonnull public static SAPDocumentTranslationApplyToSelector templateRoles( @Nonnull final TemplateRole... roles) { - return templateRoles(List.of(roles)); - } - - /** - * Start an {@code apply_to} selector for prompt template message roles. - * - * @param roles The template roles to translate. - * @return A selector with {@code category=template_roles} and the given items. - */ - @Nonnull - public static SAPDocumentTranslationApplyToSelector templateRoles( - @Nonnull final Collection roles) { final var roleStrings = - roles.stream().filter(Objects::nonNull).map(TemplateRole::getValue).toList(); + Stream.of(roles).filter(Objects::nonNull).map(TemplateRole::getValue).toList(); return SAPDocumentTranslationApplyToSelector.create() .category(TEMPLATE_ROLES) diff --git a/orchestration/src/test/java/com/sap/ai/sdk/orchestration/OrchestrationModuleConfigTest.java b/orchestration/src/test/java/com/sap/ai/sdk/orchestration/OrchestrationModuleConfigTest.java index 4e21555fc..639e288e5 100644 --- a/orchestration/src/test/java/com/sap/ai/sdk/orchestration/OrchestrationModuleConfigTest.java +++ b/orchestration/src/test/java/com/sap/ai/sdk/orchestration/OrchestrationModuleConfigTest.java @@ -1,5 +1,10 @@ package com.sap.ai.sdk.orchestration; +import static com.sap.ai.sdk.orchestration.ApplyTo.TemplateRole.ASSISTANT; +import static com.sap.ai.sdk.orchestration.ApplyTo.TemplateRole.DEVELOPER; +import static com.sap.ai.sdk.orchestration.ApplyTo.TemplateRole.SYSTEM; +import static com.sap.ai.sdk.orchestration.ApplyTo.TemplateRole.TOOL; +import static com.sap.ai.sdk.orchestration.ApplyTo.TemplateRole.USER; import static com.sap.ai.sdk.orchestration.AzureFilterThreshold.ALLOW_SAFE_LOW_MEDIUM; import static com.sap.ai.sdk.orchestration.OrchestrationAiModel.GPT_4O; import static com.sap.ai.sdk.orchestration.OrchestrationAiModel.Parameter.MAX_TOKENS; @@ -201,13 +206,7 @@ void testTranslationConfigApplyToSelectors() { assertThat(sapEmpty.getConfig().getApplyTo()).isEmpty(); selector = - ApplyTo.templateRoles( - ApplyTo.TemplateRole.USER, - ApplyTo.TemplateRole.SYSTEM, - ApplyTo.TemplateRole.ASSISTANT, - ApplyTo.TemplateRole.DEVELOPER, - ApplyTo.TemplateRole.TOOL) - .sourceLanguage("de-DE"); + ApplyTo.templateRoles(USER, SYSTEM, ASSISTANT, DEVELOPER, TOOL).sourceLanguage("de-DE"); assertThat(selector.getCategory().getValue()).isEqualTo("template_roles"); assertThat(selector.getItems()) From 2d3ef5b48f5b46aa874937980782fd579f581c54 Mon Sep 17 00:00:00 2001 From: SAP Cloud SDK Bot Date: Wed, 25 Feb 2026 12:54:23 +0000 Subject: [PATCH 13/14] Formatting --- .../src/main/java/com/sap/ai/sdk/orchestration/ApplyTo.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/ApplyTo.java b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/ApplyTo.java index 10a55e0eb..67fe72b8f 100644 --- a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/ApplyTo.java +++ b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/ApplyTo.java @@ -4,8 +4,6 @@ import static com.sap.ai.sdk.orchestration.model.SAPDocumentTranslationApplyToSelector.CategoryEnum.TEMPLATE_ROLES; import com.sap.ai.sdk.orchestration.model.SAPDocumentTranslationApplyToSelector; -import java.util.Collection; -import java.util.List; import java.util.Objects; import java.util.stream.Stream; import javax.annotation.Nonnull; From e6ebe2f3317f5317240b7044d04097aa6083efb0 Mon Sep 17 00:00:00 2001 From: Nourhan Shata Date: Wed, 25 Feb 2026 14:02:06 +0100 Subject: [PATCH 14/14] updating constructors --- .../main/java/com/sap/ai/sdk/orchestration/ApplyTo.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/ApplyTo.java b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/ApplyTo.java index 10a55e0eb..65f30c87b 100644 --- a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/ApplyTo.java +++ b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/ApplyTo.java @@ -10,6 +10,7 @@ import java.util.stream.Stream; import javax.annotation.Nonnull; import lombok.Getter; +import lombok.RequiredArgsConstructor; /** * Convenience builder for {@link SAPDocumentTranslationApplyToSelector}. @@ -17,13 +18,12 @@ *

This avoids passing raw strings for template roles and keeps sample-code readable. */ public final class ApplyTo { - private ApplyTo() {} - /** * Supported values for {@code items[]} when {@code category=template_roles}. * *

These map to the roles used in prompt templates. */ + @RequiredArgsConstructor public enum TemplateRole { /** Template role for user messages. */ USER("user"), @@ -41,10 +41,6 @@ public enum TemplateRole { TOOL("tool"); @Getter private final String value; - - TemplateRole(@Nonnull final String value) { - this.value = value; - } } /**