From 321833db7f3b46a4f6c46c5134b5a4f061d8071d Mon Sep 17 00:00:00 2001 From: Nourhan Shata Date: Wed, 23 Jul 2025 13:17:29 +0200 Subject: [PATCH 01/17] Implementation to be able to use Prompt Registry for SpringAi --- orchestration/pom.xml | 4 ++ .../spring/OrchestrationSpringUtil.java | 54 +++++++++++++++++++ .../SpringAiOrchestrationService.java | 17 ++++++ .../SpringAiOrchestrationTest.java | 10 ++++ 4 files changed, 85 insertions(+) create mode 100644 orchestration/src/main/java/com/sap/ai/sdk/orchestration/spring/OrchestrationSpringUtil.java diff --git a/orchestration/pom.xml b/orchestration/pom.xml index c80549894..80c9bf984 100644 --- a/orchestration/pom.xml +++ b/orchestration/pom.xml @@ -154,6 +154,10 @@ javaparser-core test + + com.sap.ai.sdk + prompt-registry + diff --git a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/spring/OrchestrationSpringUtil.java b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/spring/OrchestrationSpringUtil.java new file mode 100644 index 000000000..080a451ae --- /dev/null +++ b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/spring/OrchestrationSpringUtil.java @@ -0,0 +1,54 @@ +package com.sap.ai.sdk.orchestration.spring; + +import com.sap.ai.sdk.prompt.registry.PromptClient; +import com.sap.ai.sdk.prompt.registry.model.PromptTemplateSubstitutionRequest; +import com.sap.ai.sdk.prompt.registry.model.SingleChatTemplate; +import com.sap.ai.sdk.prompt.registry.model.Template; +import org.springframework.ai.chat.messages.AssistantMessage; +import org.springframework.ai.chat.messages.Message; +import org.springframework.ai.chat.messages.SystemMessage; +import org.springframework.ai.chat.messages.UserMessage; +import org.springframework.ai.chat.prompt.Prompt; + +import java.util.Map; +import java.util.UUID; + +public class OrchestrationSpringUtil { + private OrchestrationSpringUtil() { + // Utility class, no instantiation allowed + } + + public static Prompt getPromptTemplate(String templateName, Map inputParams) { + var templateMessages = + new PromptClient() + .parsePromptTemplateByNameVersion( + "categorization", + "1.0.0", + "PROMPT-CHART", + "default", + false, + PromptTemplateSubstitutionRequest.create() + .inputParams(Map.of("current_timestamp", System.currentTimeMillis()))) + .getParsedPrompt(); + + // TRANSFORM TEMPLATE TO SPRING AI MESSAGES + var messages = + templateMessages.stream() + .map( + (Template t) -> { + SingleChatTemplate message = (SingleChatTemplate) t; + return (Message) + switch (message.getRole()) { + case "system" -> new SystemMessage(message.getContent()); + case "user" -> new UserMessage(message.getContent()); + case "assistant" -> new AssistantMessage(message.getContent()); + default -> + throw new IllegalArgumentException( + "Unknown role: " + message.getRole()); + }; + }) + .toList(); + + return new Prompt(messages); + } +} diff --git a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiOrchestrationService.java b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiOrchestrationService.java index 689bdfd5d..0943e375b 100644 --- a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiOrchestrationService.java +++ b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiOrchestrationService.java @@ -19,6 +19,8 @@ import java.util.Objects; import javax.annotation.Nonnull; import javax.annotation.Nullable; + +import com.sap.ai.sdk.orchestration.spring.OrchestrationSpringUtil; import lombok.val; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor; @@ -220,4 +222,19 @@ public Translation responseFormat() { return cl.prompt(prompt).call().entity(Translation.class); } + + @Nullable + public ChatResponse getPromptTemplate() { + ChatModel client = new OrchestrationChatModel(); + var memory = new InMemoryChatMemory(); + var advisor = new MessageChatMemoryAdvisor(memory); + var cl = ChatClient.builder(client).defaultAdvisors(advisor).build(); + + val prompt = OrchestrationSpringUtil.getPromptTemplate( + "PROMPT-CHART", + Map.of("current_timestamp", System.currentTimeMillis())); + + var response = cl.prompt(prompt).call(); + return response.chatResponse(); + } } diff --git a/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/SpringAiOrchestrationTest.java b/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/SpringAiOrchestrationTest.java index 175e8aaa1..3962809f6 100644 --- a/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/SpringAiOrchestrationTest.java +++ b/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/SpringAiOrchestrationTest.java @@ -166,4 +166,14 @@ void testResponseFormat() { assertThat(translation.translation()).isNotEmpty(); assertThat(translation.language()).containsIgnoringCase("dutch"); } + + @Test + void getPromptTemplate() { + var ChatResponse = service.getPromptTemplate(); + assertThat(ChatResponse).isNotNull(); + assertThat(ChatResponse.getResult().getOutput().getText()).isNotEmpty(); + assertThat(ChatResponse.getResult().getOutput().getText()) + .contains("The current timestamp is"); + } + } From 4c88656a7a934a5c2554f51a4fb03a5b74f94b6a Mon Sep 17 00:00:00 2001 From: Nourhan Shata Date: Mon, 18 Aug 2025 11:32:51 +0200 Subject: [PATCH 02/17] Fixing Implementation of using Prompt Registry Templates in SpringAI Module. --- orchestration/pom.xml | 17 +++++--- .../spring/OrchestrationSpringUtil.java | 42 +++++++++++++------ .../SpringAiOrchestrationService.java | 25 ++++++----- .../SpringAiOrchestrationTest.java | 3 +- 4 files changed, 56 insertions(+), 31 deletions(-) diff --git a/orchestration/pom.xml b/orchestration/pom.xml index f87821a45..fb52e199a 100644 --- a/orchestration/pom.xml +++ b/orchestration/pom.xml @@ -8,7 +8,8 @@ orchestration Orchestration client - SAP Cloud SDK for AI is the official Software Development Kit (SDK) for SAP AI Core, SAP Generative AI Hub, and Orchestration Service. This is the client for the Orchestration Service. + SAP Cloud SDK for AI is the official Software Development Kit (SDK) for SAP AI Core, SAP Generative AI + Hub, and Orchestration Service. This is the client for the Orchestration Service. https://github.com/SAP/ai-sdk-java?tab=readme-ov-file#documentation @@ -36,12 +37,12 @@ ${project.basedir}/../ - 80% - 94% - 94% - 75% + 79% + 93% + 92% + 70% 93% - 100% + 97% @@ -161,6 +162,10 @@ true + + com.sap.ai.sdk + prompt-registry + diff --git a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/spring/OrchestrationSpringUtil.java b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/spring/OrchestrationSpringUtil.java index 080a451ae..6a1b83e52 100644 --- a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/spring/OrchestrationSpringUtil.java +++ b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/spring/OrchestrationSpringUtil.java @@ -1,42 +1,59 @@ package com.sap.ai.sdk.orchestration.spring; +import static com.sap.ai.sdk.orchestration.OrchestrationAiModel.GPT_4O_MINI; + +import com.sap.ai.sdk.orchestration.OrchestrationModuleConfig; import com.sap.ai.sdk.prompt.registry.PromptClient; import com.sap.ai.sdk.prompt.registry.model.PromptTemplateSubstitutionRequest; import com.sap.ai.sdk.prompt.registry.model.SingleChatTemplate; import com.sap.ai.sdk.prompt.registry.model.Template; +import java.util.Map; +import javax.annotation.Nonnull; +import lombok.val; import org.springframework.ai.chat.messages.AssistantMessage; import org.springframework.ai.chat.messages.Message; import org.springframework.ai.chat.messages.SystemMessage; import org.springframework.ai.chat.messages.UserMessage; import org.springframework.ai.chat.prompt.Prompt; -import java.util.Map; -import java.util.UUID; - +/** Utility class for orchestration-related operations in a Spring context. */ public class OrchestrationSpringUtil { + private static final OrchestrationModuleConfig config = + new OrchestrationModuleConfig().withLlmConfig(GPT_4O_MINI); + private static final OrchestrationChatOptions defaultOptions = + new OrchestrationChatOptions(config); + private OrchestrationSpringUtil() { // Utility class, no instantiation allowed } - public static Prompt getPromptTemplate(String templateName, Map inputParams) { - var templateMessages = + /** + * Get a prompt template by name and input parameters. + * + * @param templateName the name of the prompt template + * @param inputParams a map of input parameters to substitute in the template + * @return a Prompt object containing the messages from the template + */ + @Nonnull + public static Prompt getPromptTemplate( + @Nonnull final String templateName, @Nonnull final Map inputParams) { + val templateMessages = new PromptClient() .parsePromptTemplateByNameVersion( - "categorization", + "MyScenario", "1.0.0", - "PROMPT-CHART", + templateName, "default", false, - PromptTemplateSubstitutionRequest.create() - .inputParams(Map.of("current_timestamp", System.currentTimeMillis()))) + PromptTemplateSubstitutionRequest.create().inputParams(inputParams)) .getParsedPrompt(); // TRANSFORM TEMPLATE TO SPRING AI MESSAGES - var messages = + val messages = templateMessages.stream() .map( (Template t) -> { - SingleChatTemplate message = (SingleChatTemplate) t; + final SingleChatTemplate message = (SingleChatTemplate) t; return (Message) switch (message.getRole()) { case "system" -> new SystemMessage(message.getContent()); @@ -48,7 +65,6 @@ public static Prompt getPromptTemplate(String templateName, Map }; }) .toList(); - - return new Prompt(messages); + return new Prompt(messages, defaultOptions); } } diff --git a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiOrchestrationService.java b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiOrchestrationService.java index 22e4a921f..daac5c5df 100644 --- a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiOrchestrationService.java +++ b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiOrchestrationService.java @@ -15,13 +15,12 @@ import com.sap.ai.sdk.orchestration.model.DPIEntities; import com.sap.ai.sdk.orchestration.spring.OrchestrationChatModel; import com.sap.ai.sdk.orchestration.spring.OrchestrationChatOptions; +import com.sap.ai.sdk.orchestration.spring.OrchestrationSpringUtil; import java.util.List; import java.util.Map; import java.util.Objects; import javax.annotation.Nonnull; import javax.annotation.Nullable; - -import com.sap.ai.sdk.orchestration.spring.OrchestrationSpringUtil; import lombok.val; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor; @@ -273,18 +272,24 @@ public Translation responseFormat() { return cl.prompt(prompt).call().entity(Translation.class); } + /** + * Get a prompt template by name and input parameters. + * + * @return the chat response containing the prompt template + */ @Nullable public ChatResponse getPromptTemplate() { - ChatModel client = new OrchestrationChatModel(); - var memory = new InMemoryChatMemory(); - var advisor = new MessageChatMemoryAdvisor(memory); - var cl = ChatClient.builder(client).defaultAdvisors(advisor).build(); + val repository = new InMemoryChatMemoryRepository(); + val memory = MessageWindowChatMemory.builder().chatMemoryRepository(repository).build(); + val advisor = MessageChatMemoryAdvisor.builder(memory).build(); + val cl = ChatClient.builder(client).defaultAdvisors(advisor).build(); - val prompt = OrchestrationSpringUtil.getPromptTemplate( - "PROMPT-CHART", - Map.of("current_timestamp", System.currentTimeMillis())); + val prompt = + OrchestrationSpringUtil.getPromptTemplate( + "prompt_template_name", + Map.of("current_timestamp", System.currentTimeMillis(), "topic", "Time")); - var response = cl.prompt(prompt).call(); + val response = cl.prompt(prompt).call(); return response.chatResponse(); } } diff --git a/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/SpringAiOrchestrationTest.java b/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/SpringAiOrchestrationTest.java index 3debad9be..043d5db54 100644 --- a/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/SpringAiOrchestrationTest.java +++ b/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/SpringAiOrchestrationTest.java @@ -173,7 +173,6 @@ void getPromptTemplate() { assertThat(ChatResponse).isNotNull(); assertThat(ChatResponse.getResult().getOutput().getText()).isNotEmpty(); assertThat(ChatResponse.getResult().getOutput().getText()) - .contains("The current timestamp is"); + .contains("How can I assist you today?"); } - } From 616e4e37e44550367d149c9c5ed97c8af3763fab Mon Sep 17 00:00:00 2001 From: Nourhan Shata Date: Mon, 18 Aug 2025 15:19:19 +0200 Subject: [PATCH 03/17] Handling JsonProcessingException --- .../sap/ai/sdk/app/services/SpringAiOrchestrationService.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiOrchestrationService.java b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiOrchestrationService.java index daac5c5df..f979d74c2 100644 --- a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiOrchestrationService.java +++ b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiOrchestrationService.java @@ -288,8 +288,6 @@ public ChatResponse getPromptTemplate() { OrchestrationSpringUtil.getPromptTemplate( "prompt_template_name", Map.of("current_timestamp", System.currentTimeMillis(), "topic", "Time")); - - val response = cl.prompt(prompt).call(); - return response.chatResponse(); + return cl.prompt(prompt).call().chatResponse(); } } From 7e6aaec091ec5ffcbcf0d82672eec5e68e93f5d0 Mon Sep 17 00:00:00 2001 From: Nourhan Shata Date: Tue, 19 Aug 2025 13:53:26 +0200 Subject: [PATCH 04/17] Handling JsonProcessingException --- .../spring/OrchestrationChatModel.java | 1 - .../spring/OrchestrationSpringUtil.java | 19 ++++++++++--------- .../SpringAiOrchestrationService.java | 8 +++++--- .../SpringAiOrchestrationTest.java | 2 +- 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/spring/OrchestrationChatModel.java b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/spring/OrchestrationChatModel.java index c748773f3..a69c41599 100644 --- a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/spring/OrchestrationChatModel.java +++ b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/spring/OrchestrationChatModel.java @@ -64,7 +64,6 @@ public OrchestrationChatModel(@Nonnull final OrchestrationClient client) { @Override public ChatResponse call(@Nonnull final Prompt prompt) { if (prompt.getOptions() instanceof OrchestrationChatOptions options) { - val orchestrationPrompt = toOrchestrationPrompt(prompt); val response = new OrchestrationSpringChatResponse( diff --git a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/spring/OrchestrationSpringUtil.java b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/spring/OrchestrationSpringUtil.java index 6a1b83e52..91e76e340 100644 --- a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/spring/OrchestrationSpringUtil.java +++ b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/spring/OrchestrationSpringUtil.java @@ -7,6 +7,8 @@ import com.sap.ai.sdk.prompt.registry.model.PromptTemplateSubstitutionRequest; import com.sap.ai.sdk.prompt.registry.model.SingleChatTemplate; import com.sap.ai.sdk.prompt.registry.model.Template; + +import java.util.List; import java.util.Map; import javax.annotation.Nonnull; import lombok.val; @@ -18,10 +20,6 @@ /** Utility class for orchestration-related operations in a Spring context. */ public class OrchestrationSpringUtil { - private static final OrchestrationModuleConfig config = - new OrchestrationModuleConfig().withLlmConfig(GPT_4O_MINI); - private static final OrchestrationChatOptions defaultOptions = - new OrchestrationChatOptions(config); private OrchestrationSpringUtil() { // Utility class, no instantiation allowed @@ -35,13 +33,16 @@ private OrchestrationSpringUtil() { * @return a Prompt object containing the messages from the template */ @Nonnull - public static Prompt getPromptTemplate( - @Nonnull final String templateName, @Nonnull final Map inputParams) { + public static List getPromptTemplate( + @Nonnull final String templateName, + @Nonnull final String Scenario, + @Nonnull final String Version, + @Nonnull final Map inputParams) { val templateMessages = new PromptClient() .parsePromptTemplateByNameVersion( - "MyScenario", - "1.0.0", + Scenario, + Version, templateName, "default", false, @@ -65,6 +66,6 @@ public static Prompt getPromptTemplate( }; }) .toList(); - return new Prompt(messages, defaultOptions); + return messages; } } diff --git a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiOrchestrationService.java b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiOrchestrationService.java index f979d74c2..52312a4a0 100644 --- a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiOrchestrationService.java +++ b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiOrchestrationService.java @@ -26,6 +26,7 @@ import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor; import org.springframework.ai.chat.memory.InMemoryChatMemoryRepository; import org.springframework.ai.chat.memory.MessageWindowChatMemory; +import org.springframework.ai.chat.messages.Message; import org.springframework.ai.chat.messages.SystemMessage; import org.springframework.ai.chat.messages.UserMessage; import org.springframework.ai.chat.model.ChatModel; @@ -284,10 +285,11 @@ public ChatResponse getPromptTemplate() { val advisor = MessageChatMemoryAdvisor.builder(memory).build(); val cl = ChatClient.builder(client).defaultAdvisors(advisor).build(); - val prompt = + List m = OrchestrationSpringUtil.getPromptTemplate( - "prompt_template_name", - Map.of("current_timestamp", System.currentTimeMillis(), "topic", "Time")); + "prompt_template_name", "MyScenario", "1.0.0", + Map.of("current_timestamp", String.valueOf(System.currentTimeMillis()), "topic", "Time")); + val prompt = new Prompt(m, defaultOptions); return cl.prompt(prompt).call().chatResponse(); } } diff --git a/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/SpringAiOrchestrationTest.java b/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/SpringAiOrchestrationTest.java index 043d5db54..cd4e6c971 100644 --- a/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/SpringAiOrchestrationTest.java +++ b/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/SpringAiOrchestrationTest.java @@ -173,6 +173,6 @@ void getPromptTemplate() { assertThat(ChatResponse).isNotNull(); assertThat(ChatResponse.getResult().getOutput().getText()).isNotEmpty(); assertThat(ChatResponse.getResult().getOutput().getText()) - .contains("How can I assist you today?"); + .contains("blabla"); } } From 2162a9a991454619fdeb370c848dbfb75bd6d4a5 Mon Sep 17 00:00:00 2001 From: SAP Cloud SDK Bot Date: Tue, 19 Aug 2025 11:54:05 +0000 Subject: [PATCH 05/17] Formatting --- .../sdk/orchestration/spring/OrchestrationSpringUtil.java | 5 ----- .../ai/sdk/app/services/SpringAiOrchestrationService.java | 7 +++++-- .../ai/sdk/app/controllers/SpringAiOrchestrationTest.java | 3 +-- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/spring/OrchestrationSpringUtil.java b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/spring/OrchestrationSpringUtil.java index 91e76e340..f989473b5 100644 --- a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/spring/OrchestrationSpringUtil.java +++ b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/spring/OrchestrationSpringUtil.java @@ -1,13 +1,9 @@ package com.sap.ai.sdk.orchestration.spring; -import static com.sap.ai.sdk.orchestration.OrchestrationAiModel.GPT_4O_MINI; - -import com.sap.ai.sdk.orchestration.OrchestrationModuleConfig; import com.sap.ai.sdk.prompt.registry.PromptClient; import com.sap.ai.sdk.prompt.registry.model.PromptTemplateSubstitutionRequest; import com.sap.ai.sdk.prompt.registry.model.SingleChatTemplate; import com.sap.ai.sdk.prompt.registry.model.Template; - import java.util.List; import java.util.Map; import javax.annotation.Nonnull; @@ -16,7 +12,6 @@ import org.springframework.ai.chat.messages.Message; import org.springframework.ai.chat.messages.SystemMessage; import org.springframework.ai.chat.messages.UserMessage; -import org.springframework.ai.chat.prompt.Prompt; /** Utility class for orchestration-related operations in a Spring context. */ public class OrchestrationSpringUtil { diff --git a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiOrchestrationService.java b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiOrchestrationService.java index 52312a4a0..14fc9a268 100644 --- a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiOrchestrationService.java +++ b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiOrchestrationService.java @@ -287,8 +287,11 @@ public ChatResponse getPromptTemplate() { List m = OrchestrationSpringUtil.getPromptTemplate( - "prompt_template_name", "MyScenario", "1.0.0", - Map.of("current_timestamp", String.valueOf(System.currentTimeMillis()), "topic", "Time")); + "prompt_template_name", + "MyScenario", + "1.0.0", + Map.of( + "current_timestamp", String.valueOf(System.currentTimeMillis()), "topic", "Time")); val prompt = new Prompt(m, defaultOptions); return cl.prompt(prompt).call().chatResponse(); } diff --git a/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/SpringAiOrchestrationTest.java b/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/SpringAiOrchestrationTest.java index cd4e6c971..6c93db20a 100644 --- a/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/SpringAiOrchestrationTest.java +++ b/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/SpringAiOrchestrationTest.java @@ -172,7 +172,6 @@ void getPromptTemplate() { var ChatResponse = service.getPromptTemplate(); assertThat(ChatResponse).isNotNull(); assertThat(ChatResponse.getResult().getOutput().getText()).isNotEmpty(); - assertThat(ChatResponse.getResult().getOutput().getText()) - .contains("blabla"); + assertThat(ChatResponse.getResult().getOutput().getText()).contains("blabla"); } } From 63b91f1eb46d966fbecc4a087bd180c574708395 Mon Sep 17 00:00:00 2001 From: Nourhan Shata Date: Wed, 20 Aug 2025 11:37:09 +0200 Subject: [PATCH 06/17] Fixing Method ...changing approach slightly. --- .../prompt/registry/spring/SpringUtil.java | 49 +++++++++++++++++++ .../SpringAiOrchestrationService.java | 28 +++++++---- .../SpringAiOrchestrationTest.java | 7 ++- 3 files changed, 70 insertions(+), 14 deletions(-) create mode 100644 core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/spring/SpringUtil.java diff --git a/core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/spring/SpringUtil.java b/core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/spring/SpringUtil.java new file mode 100644 index 000000000..4991b0007 --- /dev/null +++ b/core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/spring/SpringUtil.java @@ -0,0 +1,49 @@ +package com.sap.ai.sdk.prompt.registry.spring; + +import com.sap.ai.sdk.prompt.registry.model.PromptTemplateSubstitutionResponse; +import com.sap.ai.sdk.prompt.registry.model.SingleChatTemplate; +import com.sap.ai.sdk.prompt.registry.model.Template; +import java.util.List; +import javax.annotation.Nonnull; +import lombok.val; +import org.springframework.ai.chat.messages.AssistantMessage; +import org.springframework.ai.chat.messages.Message; +import org.springframework.ai.chat.messages.SystemMessage; +import org.springframework.ai.chat.messages.UserMessage; + +/** Utility class for orchestration-related operations in a Spring context. */ +public class SpringUtil { + + private SpringUtil() { + // Utility class, no instantiation allowed + } + + /** + * Get a SpringAI list of messages from a Prompt Registry Response. + * + * @param promptResponse the response from Prompt Registry. + * @return list of SpringAI messages. + */ + @Nonnull + public static List promptRegistryToSpringAi( + @Nonnull final PromptTemplateSubstitutionResponse promptResponse) { + + val res = promptResponse.getParsedPrompt(); + + // TRANSFORM TEMPLATE TO SPRING AI MESSAGES + return res.stream() + .map( + (Template t) -> { + final SingleChatTemplate message = (SingleChatTemplate) t; + return (Message) + switch (message.getRole()) { + case "system" -> new SystemMessage(message.getContent()); + case "user" -> new UserMessage(message.getContent()); + case "assistant" -> new AssistantMessage(message.getContent()); + default -> + throw new IllegalArgumentException("Unknown role: " + message.getRole()); + }; + }) + .toList(); + } +} diff --git a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiOrchestrationService.java b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiOrchestrationService.java index f22480209..8d1676250 100644 --- a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiOrchestrationService.java +++ b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiOrchestrationService.java @@ -15,12 +15,15 @@ import com.sap.ai.sdk.orchestration.model.DPIEntities; import com.sap.ai.sdk.orchestration.spring.OrchestrationChatModel; import com.sap.ai.sdk.orchestration.spring.OrchestrationChatOptions; -import com.sap.ai.sdk.orchestration.spring.OrchestrationSpringUtil; +import com.sap.ai.sdk.prompt.registry.spring.SpringUtil; import java.util.List; import java.util.Map; import java.util.Objects; import javax.annotation.Nonnull; import javax.annotation.Nullable; + +import com.sap.ai.sdk.prompt.registry.PromptClient; +import com.sap.ai.sdk.prompt.registry.model.PromptTemplateSubstitutionRequest; import lombok.val; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor; @@ -279,20 +282,25 @@ public Translation responseFormat() { * @return the chat response containing the prompt template */ @Nullable - public ChatResponse getPromptTemplate() { + public ChatResponse promptRegistryToSpringAi() { val repository = new InMemoryChatMemoryRepository(); val memory = MessageWindowChatMemory.builder().chatMemoryRepository(repository).build(); val advisor = MessageChatMemoryAdvisor.builder(memory).build(); val cl = ChatClient.builder(client).defaultAdvisors(advisor).build(); - List m = - OrchestrationSpringUtil.getPromptTemplate( - "prompt_template_name", - "MyScenario", - "1.0.0", - Map.of( - "current_timestamp", String.valueOf(System.currentTimeMillis()), "topic", "Time")); - val prompt = new Prompt(m, defaultOptions); + val promptResponse = + new PromptClient() + .parsePromptTemplateByNameVersion( + "categorization", + "0.0.1", + "java-e2e-test", + "default", + false, + PromptTemplateSubstitutionRequest.create() + .inputParams(Map.of("inputExample", "I love football"))); + + List messages = SpringUtil.promptRegistryToSpringAi(promptResponse); + val prompt = new Prompt(messages, defaultOptions); return cl.prompt(prompt).call().chatResponse(); } } diff --git a/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/SpringAiOrchestrationTest.java b/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/SpringAiOrchestrationTest.java index 6c93db20a..309cb0088 100644 --- a/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/SpringAiOrchestrationTest.java +++ b/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/SpringAiOrchestrationTest.java @@ -168,10 +168,9 @@ void testResponseFormat() { } @Test - void getPromptTemplate() { - var ChatResponse = service.getPromptTemplate(); + void promptRegistryToSpringAi() { + var ChatResponse = service.promptRegistryToSpringAi(); assertThat(ChatResponse).isNotNull(); - assertThat(ChatResponse.getResult().getOutput().getText()).isNotEmpty(); - assertThat(ChatResponse.getResult().getOutput().getText()).contains("blabla"); + assertThat(ChatResponse.getResult().getOutput().getText()).contains("Sports"); } } From 0433a46124898219d3f5f2a4b4eb014027b05e1a Mon Sep 17 00:00:00 2001 From: SAP Cloud SDK Bot Date: Wed, 20 Aug 2025 09:37:56 +0000 Subject: [PATCH 07/17] Formatting --- .../ai/sdk/app/services/SpringAiOrchestrationService.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiOrchestrationService.java b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiOrchestrationService.java index 8d1676250..266ae3997 100644 --- a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiOrchestrationService.java +++ b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiOrchestrationService.java @@ -15,15 +15,14 @@ import com.sap.ai.sdk.orchestration.model.DPIEntities; import com.sap.ai.sdk.orchestration.spring.OrchestrationChatModel; import com.sap.ai.sdk.orchestration.spring.OrchestrationChatOptions; +import com.sap.ai.sdk.prompt.registry.PromptClient; +import com.sap.ai.sdk.prompt.registry.model.PromptTemplateSubstitutionRequest; import com.sap.ai.sdk.prompt.registry.spring.SpringUtil; import java.util.List; import java.util.Map; import java.util.Objects; import javax.annotation.Nonnull; import javax.annotation.Nullable; - -import com.sap.ai.sdk.prompt.registry.PromptClient; -import com.sap.ai.sdk.prompt.registry.model.PromptTemplateSubstitutionRequest; import lombok.val; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor; From 2653c1f1e4bf45b230c082e5ad5639304358c126 Mon Sep 17 00:00:00 2001 From: Nourhan Shata Date: Wed, 20 Aug 2025 15:45:14 +0200 Subject: [PATCH 08/17] Doing Unit Test and moving method to Prompt Registry. --- core-services/prompt-registry/pom.xml | 5 ++ .../PromptTemplateSubstitutionResponse.java | 2 +- .../registry/spring/SpringUtilTest.java | 51 ++++++++++++++ .../mappings/templatesInputParams.json | 26 ++++++++ .../spring/OrchestrationSpringUtil.java | 66 ------------------- .../controllers/PromptRegistryController.java | 43 ++++++++++++ .../SpringAiOrchestrationService.java | 28 -------- .../src/main/resources/static/index.html | 12 ++++ .../app/controllers/PromptRegistryTest.java | 8 +++ .../SpringAiOrchestrationTest.java | 7 -- 10 files changed, 146 insertions(+), 102 deletions(-) create mode 100644 core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/spring/SpringUtilTest.java create mode 100644 core-services/prompt-registry/src/test/resources/mappings/templatesInputParams.json delete mode 100644 orchestration/src/main/java/com/sap/ai/sdk/orchestration/spring/OrchestrationSpringUtil.java diff --git a/core-services/prompt-registry/pom.xml b/core-services/prompt-registry/pom.xml index 8fd41e9c9..6c4fa76ce 100644 --- a/core-services/prompt-registry/pom.xml +++ b/core-services/prompt-registry/pom.xml @@ -64,6 +64,11 @@ org.springframework spring-web + + org.springframework.ai + spring-ai-model + true + com.sap.cloud.sdk.cloudplatform cloudplatform-connectivity diff --git a/core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/model/PromptTemplateSubstitutionResponse.java b/core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/model/PromptTemplateSubstitutionResponse.java index 51be802a7..aa4587791 100644 --- a/core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/model/PromptTemplateSubstitutionResponse.java +++ b/core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/model/PromptTemplateSubstitutionResponse.java @@ -40,7 +40,7 @@ public class PromptTemplateSubstitutionResponse private final Map cloudSdkCustomFields = new LinkedHashMap<>(); /** Default constructor for PromptTemplateSubstitutionResponse. */ - protected PromptTemplateSubstitutionResponse() {} + public PromptTemplateSubstitutionResponse() {} /** * Set the parsedPrompt of this {@link PromptTemplateSubstitutionResponse} instance and return the diff --git a/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/spring/SpringUtilTest.java b/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/spring/SpringUtilTest.java new file mode 100644 index 000000000..abc0c5a58 --- /dev/null +++ b/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/spring/SpringUtilTest.java @@ -0,0 +1,51 @@ +package com.sap.ai.sdk.prompt.registry.spring; + +import com.github.tomakehurst.wiremock.junit5.WireMockExtension; +import com.sap.ai.sdk.core.AiCoreService; +import com.sap.ai.sdk.prompt.registry.PromptClient; +import com.sap.ai.sdk.prompt.registry.model.PromptTemplateSubstitutionRequest; +import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination; +import com.sap.cloud.sdk.cloudplatform.connectivity.HttpDestination; +import lombok.val; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.springframework.ai.chat.messages.Message; +import org.springframework.ai.chat.messages.SystemMessage; +import org.springframework.ai.chat.messages.UserMessage; + +import java.util.List; +import java.util.Map; + +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; +import static org.assertj.core.api.Assertions.assertThat; + +public class SpringUtilTest { + @RegisterExtension + private static final WireMockExtension WM = + WireMockExtension.newInstance().options(wireMockConfig().dynamicPort()).build(); + + private final HttpDestination DESTINATION = DefaultHttpDestination.builder(WM.baseUrl()).build(); + private final AiCoreService SERVICE = new AiCoreService().withBaseDestination(DESTINATION); + + @Test + void testPipelines() { + var client = new PromptClient(SERVICE); + val promptResponse = + client.parsePromptTemplateByNameVersion( + "categorization", + "0.0.1", + "java-e2e-test", + "default", + false, + PromptTemplateSubstitutionRequest.create() + .inputParams(Map.of("inputExample", "I love football"))); + + List messages = SpringUtil.promptRegistryToSpringAi(promptResponse); + assertThat(messages) + .isEqualTo( + List.of( + new SystemMessage( + "You classify input text into the two following categories: Finance, Tech, Sports, Politics"), + new UserMessage("I love football"))); + } +} diff --git a/core-services/prompt-registry/src/test/resources/mappings/templatesInputParams.json b/core-services/prompt-registry/src/test/resources/mappings/templatesInputParams.json new file mode 100644 index 000000000..ea3a55fc0 --- /dev/null +++ b/core-services/prompt-registry/src/test/resources/mappings/templatesInputParams.json @@ -0,0 +1,26 @@ +{ + "request": { + "method": "POST", + "url": "/v2/lm/scenarios/categorization/promptTemplates/java-e2e-test/versions/0.0.1/substitution?metadata=false" + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/json" + }, + "jsonBody": { + "parsedPrompt": [ + { + "role": "system", + "content": "You classify input text into the two following categories: Finance, Tech, Sports, Politics" + }, + { + "role": "user", + "content": "I love football" + } + ] + } + } +} + + diff --git a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/spring/OrchestrationSpringUtil.java b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/spring/OrchestrationSpringUtil.java deleted file mode 100644 index f989473b5..000000000 --- a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/spring/OrchestrationSpringUtil.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.sap.ai.sdk.orchestration.spring; - -import com.sap.ai.sdk.prompt.registry.PromptClient; -import com.sap.ai.sdk.prompt.registry.model.PromptTemplateSubstitutionRequest; -import com.sap.ai.sdk.prompt.registry.model.SingleChatTemplate; -import com.sap.ai.sdk.prompt.registry.model.Template; -import java.util.List; -import java.util.Map; -import javax.annotation.Nonnull; -import lombok.val; -import org.springframework.ai.chat.messages.AssistantMessage; -import org.springframework.ai.chat.messages.Message; -import org.springframework.ai.chat.messages.SystemMessage; -import org.springframework.ai.chat.messages.UserMessage; - -/** Utility class for orchestration-related operations in a Spring context. */ -public class OrchestrationSpringUtil { - - private OrchestrationSpringUtil() { - // Utility class, no instantiation allowed - } - - /** - * Get a prompt template by name and input parameters. - * - * @param templateName the name of the prompt template - * @param inputParams a map of input parameters to substitute in the template - * @return a Prompt object containing the messages from the template - */ - @Nonnull - public static List getPromptTemplate( - @Nonnull final String templateName, - @Nonnull final String Scenario, - @Nonnull final String Version, - @Nonnull final Map inputParams) { - val templateMessages = - new PromptClient() - .parsePromptTemplateByNameVersion( - Scenario, - Version, - templateName, - "default", - false, - PromptTemplateSubstitutionRequest.create().inputParams(inputParams)) - .getParsedPrompt(); - - // TRANSFORM TEMPLATE TO SPRING AI MESSAGES - val messages = - templateMessages.stream() - .map( - (Template t) -> { - final SingleChatTemplate message = (SingleChatTemplate) t; - return (Message) - switch (message.getRole()) { - case "system" -> new SystemMessage(message.getContent()); - case "user" -> new UserMessage(message.getContent()); - case "assistant" -> new AssistantMessage(message.getContent()); - default -> - throw new IllegalArgumentException( - "Unknown role: " + message.getRole()); - }; - }) - .toList(); - return messages; - } -} diff --git a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/controllers/PromptRegistryController.java b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/controllers/PromptRegistryController.java index cf8fdb513..a99162f42 100644 --- a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/controllers/PromptRegistryController.java +++ b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/controllers/PromptRegistryController.java @@ -1,5 +1,8 @@ package com.sap.ai.sdk.app.controllers; +import com.sap.ai.sdk.foundationmodels.openai.OpenAiClient; +import com.sap.ai.sdk.foundationmodels.openai.OpenAiModel; +import com.sap.ai.sdk.foundationmodels.openai.spring.OpenAiChatModel; import com.sap.ai.sdk.prompt.registry.PromptClient; import com.sap.ai.sdk.prompt.registry.model.PromptTemplateDeleteResponse; import com.sap.ai.sdk.prompt.registry.model.PromptTemplateListResponse; @@ -13,12 +16,27 @@ import java.io.IOException; import java.util.List; import java.util.Map; + +import com.sap.ai.sdk.prompt.registry.spring.SpringUtil; +import lombok.val; +import org.springframework.ai.chat.client.ChatClient; +import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor; +import org.springframework.ai.chat.memory.InMemoryChatMemoryRepository; +import org.springframework.ai.chat.memory.MessageWindowChatMemory; +import org.springframework.ai.chat.messages.Message; +import org.springframework.ai.chat.model.ChatModel; +import org.springframework.ai.chat.model.ChatResponse; +import org.springframework.ai.chat.model.Generation; +import org.springframework.ai.chat.prompt.Prompt; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import javax.annotation.Nullable; + /** Endpoint for Prompt Registry operations */ @SuppressWarnings("unused") // debug class that doesn't need to be tested @RestController @@ -99,4 +117,29 @@ List deleteTemplate() { .map(template -> client.deletePromptTemplate(template.getId())) .toList(); } + + @GetMapping("/promptRegistryToSpringAi") + Generation promptRegistryToSpringAi() { + ChatModel openAiClient = new OpenAiChatModel(OpenAiClient.forModel(OpenAiModel.GPT_4O_MINI)); + val repository = new InMemoryChatMemoryRepository(); + val memory = MessageWindowChatMemory.builder().chatMemoryRepository(repository).build(); + val advisor = MessageChatMemoryAdvisor.builder(memory).build(); + val cl = ChatClient.builder(openAiClient).defaultAdvisors(advisor).build(); + + val promptResponse = + new PromptClient() + .parsePromptTemplateByNameVersion( + "categorization", + "0.0.1", + "java-e2e-test", + "default", + false, + PromptTemplateSubstitutionRequest.create() + .inputParams(Map.of("inputExample", "I love football"))); + + List messages = SpringUtil.promptRegistryToSpringAi(promptResponse); + val prompt = new Prompt(messages); + val response = cl.prompt(prompt).call().chatResponse(); + return response != null ? response.getResult() : null; + } } diff --git a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiOrchestrationService.java b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiOrchestrationService.java index 8d1676250..fa652c9b5 100644 --- a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiOrchestrationService.java +++ b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiOrchestrationService.java @@ -275,32 +275,4 @@ public Translation responseFormat() { return cl.prompt(prompt).call().entity(Translation.class); } - - /** - * Get a prompt template by name and input parameters. - * - * @return the chat response containing the prompt template - */ - @Nullable - public ChatResponse promptRegistryToSpringAi() { - val repository = new InMemoryChatMemoryRepository(); - val memory = MessageWindowChatMemory.builder().chatMemoryRepository(repository).build(); - val advisor = MessageChatMemoryAdvisor.builder(memory).build(); - val cl = ChatClient.builder(client).defaultAdvisors(advisor).build(); - - val promptResponse = - new PromptClient() - .parsePromptTemplateByNameVersion( - "categorization", - "0.0.1", - "java-e2e-test", - "default", - false, - PromptTemplateSubstitutionRequest.create() - .inputParams(Map.of("inputExample", "I love football"))); - - List messages = SpringUtil.promptRegistryToSpringAi(promptResponse); - val prompt = new Prompt(messages, defaultOptions); - return cl.prompt(prompt).call().chatResponse(); - } } 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 61e2a9f68..62a5f9052 100644 --- a/sample-code/spring-app/src/main/resources/static/index.html +++ b/sample-code/spring-app/src/main/resources/static/index.html @@ -1051,6 +1051,18 @@

📚 Prompt Registry

+
  • +
    + +
    + Get a SpringAI list of messages from a Prompt Registry Response. +
    +
    +
  • diff --git a/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/PromptRegistryTest.java b/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/PromptRegistryTest.java index 1758fc8dc..2854a5a97 100644 --- a/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/PromptRegistryTest.java +++ b/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/PromptRegistryTest.java @@ -85,4 +85,12 @@ void history() { assertThat(deletedTemplate).hasSize(1); assertThat(deletedTemplate.get(0).getMessage()).contains("successful"); } + + @Test + void promptRegistryToSpringAi() { + var controller = new PromptRegistryController(); + var ChatResponse = controller.promptRegistryToSpringAi(); + assertThat(ChatResponse).isNotNull(); + assertThat(ChatResponse.getOutput().getText()).contains("Sports"); + } } diff --git a/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/SpringAiOrchestrationTest.java b/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/SpringAiOrchestrationTest.java index 309cb0088..450a10fda 100644 --- a/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/SpringAiOrchestrationTest.java +++ b/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/SpringAiOrchestrationTest.java @@ -166,11 +166,4 @@ void testResponseFormat() { assertThat(translation.translation()).isNotEmpty(); assertThat(translation.language()).containsIgnoringCase("dutch"); } - - @Test - void promptRegistryToSpringAi() { - var ChatResponse = service.promptRegistryToSpringAi(); - assertThat(ChatResponse).isNotNull(); - assertThat(ChatResponse.getResult().getOutput().getText()).contains("Sports"); - } } From 1ec76f6450813dd7e754c5d082f18dccbf34866b Mon Sep 17 00:00:00 2001 From: SAP Cloud SDK Bot Date: Wed, 20 Aug 2025 13:46:20 +0000 Subject: [PATCH 09/17] Formatting --- .../ai/sdk/prompt/registry/spring/SpringUtilTest.java | 11 +++++------ .../sdk/app/controllers/PromptRegistryController.java | 7 +------ .../app/services/SpringAiOrchestrationService.java | 4 ---- 3 files changed, 6 insertions(+), 16 deletions(-) diff --git a/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/spring/SpringUtilTest.java b/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/spring/SpringUtilTest.java index abc0c5a58..4a269f7bb 100644 --- a/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/spring/SpringUtilTest.java +++ b/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/spring/SpringUtilTest.java @@ -1,11 +1,16 @@ package com.sap.ai.sdk.prompt.registry.spring; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; +import static org.assertj.core.api.Assertions.assertThat; + import com.github.tomakehurst.wiremock.junit5.WireMockExtension; import com.sap.ai.sdk.core.AiCoreService; import com.sap.ai.sdk.prompt.registry.PromptClient; import com.sap.ai.sdk.prompt.registry.model.PromptTemplateSubstitutionRequest; import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination; import com.sap.cloud.sdk.cloudplatform.connectivity.HttpDestination; +import java.util.List; +import java.util.Map; import lombok.val; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -13,12 +18,6 @@ import org.springframework.ai.chat.messages.SystemMessage; import org.springframework.ai.chat.messages.UserMessage; -import java.util.List; -import java.util.Map; - -import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; -import static org.assertj.core.api.Assertions.assertThat; - public class SpringUtilTest { @RegisterExtension private static final WireMockExtension WM = diff --git a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/controllers/PromptRegistryController.java b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/controllers/PromptRegistryController.java index a99162f42..4c50585ba 100644 --- a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/controllers/PromptRegistryController.java +++ b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/controllers/PromptRegistryController.java @@ -12,12 +12,11 @@ import com.sap.ai.sdk.prompt.registry.model.PromptTemplateSubstitutionRequest; import com.sap.ai.sdk.prompt.registry.model.PromptTemplateSubstitutionResponse; import com.sap.ai.sdk.prompt.registry.model.SingleChatTemplate; +import com.sap.ai.sdk.prompt.registry.spring.SpringUtil; import java.io.File; import java.io.IOException; import java.util.List; import java.util.Map; - -import com.sap.ai.sdk.prompt.registry.spring.SpringUtil; import lombok.val; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor; @@ -25,18 +24,14 @@ import org.springframework.ai.chat.memory.MessageWindowChatMemory; import org.springframework.ai.chat.messages.Message; import org.springframework.ai.chat.model.ChatModel; -import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.model.Generation; import org.springframework.ai.chat.prompt.Prompt; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import javax.annotation.Nullable; - /** Endpoint for Prompt Registry operations */ @SuppressWarnings("unused") // debug class that doesn't need to be tested @RestController diff --git a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiOrchestrationService.java b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiOrchestrationService.java index 17d6d4749..4cadc95f5 100644 --- a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiOrchestrationService.java +++ b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiOrchestrationService.java @@ -15,9 +15,6 @@ import com.sap.ai.sdk.orchestration.model.DPIEntities; import com.sap.ai.sdk.orchestration.spring.OrchestrationChatModel; import com.sap.ai.sdk.orchestration.spring.OrchestrationChatOptions; -import com.sap.ai.sdk.prompt.registry.PromptClient; -import com.sap.ai.sdk.prompt.registry.model.PromptTemplateSubstitutionRequest; -import com.sap.ai.sdk.prompt.registry.spring.SpringUtil; import java.util.List; import java.util.Map; import java.util.Objects; @@ -28,7 +25,6 @@ import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor; import org.springframework.ai.chat.memory.InMemoryChatMemoryRepository; import org.springframework.ai.chat.memory.MessageWindowChatMemory; -import org.springframework.ai.chat.messages.Message; import org.springframework.ai.chat.messages.SystemMessage; import org.springframework.ai.chat.messages.UserMessage; import org.springframework.ai.chat.model.ChatModel; From 7148907398ba90ec261db8ab4934971fa725b0ce Mon Sep 17 00:00:00 2001 From: Nourhan Shata Date: Wed, 20 Aug 2025 15:58:01 +0200 Subject: [PATCH 10/17] Doing Unit Test and moving method to Prompt Registry. --- .../{SpringUtil.java => SpringAiConverter.java} | 6 +++--- ...UtilTest.java => SpringAiConverterTest.java} | 4 ++-- orchestration/pom.xml | 17 ++++++----------- .../spring/OrchestrationChatModel.java | 1 + .../controllers/PromptRegistryController.java | 8 ++------ .../services/SpringAiOrchestrationService.java | 5 +---- 6 files changed, 15 insertions(+), 26 deletions(-) rename core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/spring/{SpringUtil.java => SpringAiConverter.java} (93%) rename core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/spring/{SpringUtilTest.java => SpringAiConverterTest.java} (93%) diff --git a/core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/spring/SpringUtil.java b/core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/spring/SpringAiConverter.java similarity index 93% rename from core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/spring/SpringUtil.java rename to core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/spring/SpringAiConverter.java index 4991b0007..d0307a924 100644 --- a/core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/spring/SpringUtil.java +++ b/core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/spring/SpringAiConverter.java @@ -12,9 +12,9 @@ import org.springframework.ai.chat.messages.UserMessage; /** Utility class for orchestration-related operations in a Spring context. */ -public class SpringUtil { +public class SpringAiConverter { - private SpringUtil() { + private SpringAiConverter() { // Utility class, no instantiation allowed } @@ -25,7 +25,7 @@ private SpringUtil() { * @return list of SpringAI messages. */ @Nonnull - public static List promptRegistryToSpringAi( + public static List promptTemplateToMessages( @Nonnull final PromptTemplateSubstitutionResponse promptResponse) { val res = promptResponse.getParsedPrompt(); diff --git a/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/spring/SpringUtilTest.java b/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/spring/SpringAiConverterTest.java similarity index 93% rename from core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/spring/SpringUtilTest.java rename to core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/spring/SpringAiConverterTest.java index abc0c5a58..3ac99056f 100644 --- a/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/spring/SpringUtilTest.java +++ b/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/spring/SpringAiConverterTest.java @@ -19,7 +19,7 @@ import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; import static org.assertj.core.api.Assertions.assertThat; -public class SpringUtilTest { +public class SpringAiConverterTest { @RegisterExtension private static final WireMockExtension WM = WireMockExtension.newInstance().options(wireMockConfig().dynamicPort()).build(); @@ -40,7 +40,7 @@ void testPipelines() { PromptTemplateSubstitutionRequest.create() .inputParams(Map.of("inputExample", "I love football"))); - List messages = SpringUtil.promptRegistryToSpringAi(promptResponse); + List messages = SpringAiConverter.promptTemplateToMessages(promptResponse); assertThat(messages) .isEqualTo( List.of( diff --git a/orchestration/pom.xml b/orchestration/pom.xml index fb52e199a..f87821a45 100644 --- a/orchestration/pom.xml +++ b/orchestration/pom.xml @@ -8,8 +8,7 @@ orchestration Orchestration client - SAP Cloud SDK for AI is the official Software Development Kit (SDK) for SAP AI Core, SAP Generative AI - Hub, and Orchestration Service. This is the client for the Orchestration Service. + SAP Cloud SDK for AI is the official Software Development Kit (SDK) for SAP AI Core, SAP Generative AI Hub, and Orchestration Service. This is the client for the Orchestration Service. https://github.com/SAP/ai-sdk-java?tab=readme-ov-file#documentation @@ -37,12 +36,12 @@ ${project.basedir}/../ - 79% - 93% - 92% - 70% + 80% + 94% + 94% + 75% 93% - 97% + 100% @@ -162,10 +161,6 @@ true
    - - com.sap.ai.sdk - prompt-registry - diff --git a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/spring/OrchestrationChatModel.java b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/spring/OrchestrationChatModel.java index a69c41599..c748773f3 100644 --- a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/spring/OrchestrationChatModel.java +++ b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/spring/OrchestrationChatModel.java @@ -64,6 +64,7 @@ public OrchestrationChatModel(@Nonnull final OrchestrationClient client) { @Override public ChatResponse call(@Nonnull final Prompt prompt) { if (prompt.getOptions() instanceof OrchestrationChatOptions options) { + val orchestrationPrompt = toOrchestrationPrompt(prompt); val response = new OrchestrationSpringChatResponse( diff --git a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/controllers/PromptRegistryController.java b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/controllers/PromptRegistryController.java index a99162f42..109049bf0 100644 --- a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/controllers/PromptRegistryController.java +++ b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/controllers/PromptRegistryController.java @@ -17,7 +17,7 @@ import java.util.List; import java.util.Map; -import com.sap.ai.sdk.prompt.registry.spring.SpringUtil; +import com.sap.ai.sdk.prompt.registry.spring.SpringAiConverter; import lombok.val; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor; @@ -25,18 +25,14 @@ import org.springframework.ai.chat.memory.MessageWindowChatMemory; import org.springframework.ai.chat.messages.Message; import org.springframework.ai.chat.model.ChatModel; -import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.model.Generation; import org.springframework.ai.chat.prompt.Prompt; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import javax.annotation.Nullable; - /** Endpoint for Prompt Registry operations */ @SuppressWarnings("unused") // debug class that doesn't need to be tested @RestController @@ -137,7 +133,7 @@ Generation promptRegistryToSpringAi() { PromptTemplateSubstitutionRequest.create() .inputParams(Map.of("inputExample", "I love football"))); - List messages = SpringUtil.promptRegistryToSpringAi(promptResponse); + List messages = SpringAiConverter.promptTemplateToMessages(promptResponse); val prompt = new Prompt(messages); val response = cl.prompt(prompt).call().chatResponse(); return response != null ? response.getResult() : null; diff --git a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiOrchestrationService.java b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiOrchestrationService.java index 17d6d4749..1659c273e 100644 --- a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiOrchestrationService.java +++ b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiOrchestrationService.java @@ -15,9 +15,7 @@ import com.sap.ai.sdk.orchestration.model.DPIEntities; import com.sap.ai.sdk.orchestration.spring.OrchestrationChatModel; import com.sap.ai.sdk.orchestration.spring.OrchestrationChatOptions; -import com.sap.ai.sdk.prompt.registry.PromptClient; -import com.sap.ai.sdk.prompt.registry.model.PromptTemplateSubstitutionRequest; -import com.sap.ai.sdk.prompt.registry.spring.SpringUtil; + import java.util.List; import java.util.Map; import java.util.Objects; @@ -28,7 +26,6 @@ import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor; import org.springframework.ai.chat.memory.InMemoryChatMemoryRepository; import org.springframework.ai.chat.memory.MessageWindowChatMemory; -import org.springframework.ai.chat.messages.Message; import org.springframework.ai.chat.messages.SystemMessage; import org.springframework.ai.chat.messages.UserMessage; import org.springframework.ai.chat.model.ChatModel; From 090872ded5e7092076f20d8305f1ae96869b926b Mon Sep 17 00:00:00 2001 From: SAP Cloud SDK Bot Date: Wed, 20 Aug 2025 13:59:50 +0000 Subject: [PATCH 11/17] Formatting --- .../prompt/registry/spring/SpringAiConverterTest.java | 11 +++++------ .../sdk/app/controllers/PromptRegistryController.java | 3 +-- .../app/services/SpringAiOrchestrationService.java | 1 - 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/spring/SpringAiConverterTest.java b/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/spring/SpringAiConverterTest.java index 3ac99056f..2a2082b63 100644 --- a/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/spring/SpringAiConverterTest.java +++ b/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/spring/SpringAiConverterTest.java @@ -1,11 +1,16 @@ package com.sap.ai.sdk.prompt.registry.spring; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; +import static org.assertj.core.api.Assertions.assertThat; + import com.github.tomakehurst.wiremock.junit5.WireMockExtension; import com.sap.ai.sdk.core.AiCoreService; import com.sap.ai.sdk.prompt.registry.PromptClient; import com.sap.ai.sdk.prompt.registry.model.PromptTemplateSubstitutionRequest; import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination; import com.sap.cloud.sdk.cloudplatform.connectivity.HttpDestination; +import java.util.List; +import java.util.Map; import lombok.val; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -13,12 +18,6 @@ import org.springframework.ai.chat.messages.SystemMessage; import org.springframework.ai.chat.messages.UserMessage; -import java.util.List; -import java.util.Map; - -import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; -import static org.assertj.core.api.Assertions.assertThat; - public class SpringAiConverterTest { @RegisterExtension private static final WireMockExtension WM = diff --git a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/controllers/PromptRegistryController.java b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/controllers/PromptRegistryController.java index 109049bf0..e69846380 100644 --- a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/controllers/PromptRegistryController.java +++ b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/controllers/PromptRegistryController.java @@ -12,12 +12,11 @@ import com.sap.ai.sdk.prompt.registry.model.PromptTemplateSubstitutionRequest; import com.sap.ai.sdk.prompt.registry.model.PromptTemplateSubstitutionResponse; import com.sap.ai.sdk.prompt.registry.model.SingleChatTemplate; +import com.sap.ai.sdk.prompt.registry.spring.SpringAiConverter; import java.io.File; import java.io.IOException; import java.util.List; import java.util.Map; - -import com.sap.ai.sdk.prompt.registry.spring.SpringAiConverter; import lombok.val; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor; diff --git a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiOrchestrationService.java b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiOrchestrationService.java index 1659c273e..4cadc95f5 100644 --- a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiOrchestrationService.java +++ b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiOrchestrationService.java @@ -15,7 +15,6 @@ import com.sap.ai.sdk.orchestration.model.DPIEntities; import com.sap.ai.sdk.orchestration.spring.OrchestrationChatModel; import com.sap.ai.sdk.orchestration.spring.OrchestrationChatOptions; - import java.util.List; import java.util.Map; import java.util.Objects; From b446dc293d498782df33508f72db7d2195b5b796 Mon Sep 17 00:00:00 2001 From: Nourhan Shata Date: Wed, 20 Aug 2025 16:30:53 +0200 Subject: [PATCH 12/17] Fixing Coverage. --- core-services/prompt-registry/pom.xml | 4 +-- .../spring/SpringAiConverterTest.java | 22 ++++++++++++++-- .../src/test/resources/mappings/error.json | 26 +++++++++++++++++++ .../controllers/PromptRegistryController.java | 5 ++-- 4 files changed, 50 insertions(+), 7 deletions(-) create mode 100644 core-services/prompt-registry/src/test/resources/mappings/error.json diff --git a/core-services/prompt-registry/pom.xml b/core-services/prompt-registry/pom.xml index 6c4fa76ce..53949ebb8 100644 --- a/core-services/prompt-registry/pom.xml +++ b/core-services/prompt-registry/pom.xml @@ -38,10 +38,10 @@ ${project.basedir}/../../ - 75% + 73% 87% 89% - 100% + 75% 75% 100% diff --git a/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/spring/SpringAiConverterTest.java b/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/spring/SpringAiConverterTest.java index 2a2082b63..43756abd8 100644 --- a/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/spring/SpringAiConverterTest.java +++ b/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/spring/SpringAiConverterTest.java @@ -2,7 +2,7 @@ import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; import static org.assertj.core.api.Assertions.assertThat; - +import static org.assertj.core.api.Assertions.assertThatThrownBy; import com.github.tomakehurst.wiremock.junit5.WireMockExtension; import com.sap.ai.sdk.core.AiCoreService; import com.sap.ai.sdk.prompt.registry.PromptClient; @@ -27,7 +27,7 @@ public class SpringAiConverterTest { private final AiCoreService SERVICE = new AiCoreService().withBaseDestination(DESTINATION); @Test - void testPipelines() { + void testPromptRegistryToSpringAi() { var client = new PromptClient(SERVICE); val promptResponse = client.parsePromptTemplateByNameVersion( @@ -47,4 +47,22 @@ void testPipelines() { "You classify input text into the two following categories: Finance, Tech, Sports, Politics"), new UserMessage("I love football"))); } + + @Test + void testInvalidRoleThrowsException() { + var client = new PromptClient(SERVICE); + val errorPrompt = + client.parsePromptTemplateByNameVersion( + "categorization", + "0.0.1", + "error", + "default", + false, + PromptTemplateSubstitutionRequest.create() + .inputParams(Map.of("inputExample", "I love football"))); + + assertThatThrownBy(() -> SpringAiConverter.promptTemplateToMessages(errorPrompt)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Unknown role: error"); + } } diff --git a/core-services/prompt-registry/src/test/resources/mappings/error.json b/core-services/prompt-registry/src/test/resources/mappings/error.json new file mode 100644 index 000000000..50f131be0 --- /dev/null +++ b/core-services/prompt-registry/src/test/resources/mappings/error.json @@ -0,0 +1,26 @@ +{ + "request": { + "method": "POST", + "url": "/v2/lm/scenarios/categorization/promptTemplates/error/versions/0.0.1/substitution?metadata=false" + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/json" + }, + "jsonBody": { + "parsedPrompt": [ + { + "role": "assistant", + "content": "What can I help you with?" + }, + { + "role": "error", + "content": "What is this?" + } + ] + } + } +} + + diff --git a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/controllers/PromptRegistryController.java b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/controllers/PromptRegistryController.java index e69846380..52b4b391f 100644 --- a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/controllers/PromptRegistryController.java +++ b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/controllers/PromptRegistryController.java @@ -23,7 +23,6 @@ import org.springframework.ai.chat.memory.InMemoryChatMemoryRepository; import org.springframework.ai.chat.memory.MessageWindowChatMemory; import org.springframework.ai.chat.messages.Message; -import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.chat.model.Generation; import org.springframework.ai.chat.prompt.Prompt; import org.springframework.core.io.ClassPathResource; @@ -115,7 +114,7 @@ List deleteTemplate() { @GetMapping("/promptRegistryToSpringAi") Generation promptRegistryToSpringAi() { - ChatModel openAiClient = new OpenAiChatModel(OpenAiClient.forModel(OpenAiModel.GPT_4O_MINI)); + val openAiClient = new OpenAiChatModel(OpenAiClient.forModel(OpenAiModel.GPT_4O_MINI)); val repository = new InMemoryChatMemoryRepository(); val memory = MessageWindowChatMemory.builder().chatMemoryRepository(repository).build(); val advisor = MessageChatMemoryAdvisor.builder(memory).build(); @@ -132,7 +131,7 @@ Generation promptRegistryToSpringAi() { PromptTemplateSubstitutionRequest.create() .inputParams(Map.of("inputExample", "I love football"))); - List messages = SpringAiConverter.promptTemplateToMessages(promptResponse); + final List messages = SpringAiConverter.promptTemplateToMessages(promptResponse); val prompt = new Prompt(messages); val response = cl.prompt(prompt).call().chatResponse(); return response != null ? response.getResult() : null; From c93590748223997f1645d7446750ee43084969ea Mon Sep 17 00:00:00 2001 From: SAP Cloud SDK Bot Date: Wed, 20 Aug 2025 14:31:39 +0000 Subject: [PATCH 13/17] Formatting --- .../sap/ai/sdk/prompt/registry/spring/SpringAiConverterTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/spring/SpringAiConverterTest.java b/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/spring/SpringAiConverterTest.java index 43756abd8..07d117f34 100644 --- a/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/spring/SpringAiConverterTest.java +++ b/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/spring/SpringAiConverterTest.java @@ -3,6 +3,7 @@ import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; + import com.github.tomakehurst.wiremock.junit5.WireMockExtension; import com.sap.ai.sdk.core.AiCoreService; import com.sap.ai.sdk.prompt.registry.PromptClient; From 8cb99003ca15ee0489733055b98e7d5f46a77687 Mon Sep 17 00:00:00 2001 From: Nourhan Shata Date: Wed, 20 Aug 2025 21:37:27 +0200 Subject: [PATCH 14/17] Format --- .../sap/ai/sdk/prompt/registry/spring/SpringAiConverterTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/spring/SpringAiConverterTest.java b/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/spring/SpringAiConverterTest.java index 43756abd8..07d117f34 100644 --- a/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/spring/SpringAiConverterTest.java +++ b/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/spring/SpringAiConverterTest.java @@ -3,6 +3,7 @@ import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; + import com.github.tomakehurst.wiremock.junit5.WireMockExtension; import com.sap.ai.sdk.core.AiCoreService; import com.sap.ai.sdk.prompt.registry.PromptClient; From 0b93e28abe58c6856d32bee191d6bd95ed3ed8a1 Mon Sep 17 00:00:00 2001 From: Nourhan Shata Date: Wed, 20 Aug 2025 22:24:32 +0200 Subject: [PATCH 15/17] Fixing Javadoc --- .../sap/ai/sdk/prompt/registry/spring/SpringAiConverter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/spring/SpringAiConverter.java b/core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/spring/SpringAiConverter.java index d0307a924..1d3a6926d 100644 --- a/core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/spring/SpringAiConverter.java +++ b/core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/spring/SpringAiConverter.java @@ -11,7 +11,7 @@ import org.springframework.ai.chat.messages.SystemMessage; import org.springframework.ai.chat.messages.UserMessage; -/** Utility class for orchestration-related operations in a Spring context. */ +/** Utility class for prompt registry related operations in a Spring context. */ public class SpringAiConverter { private SpringAiConverter() { From 4622ca01cccedc999f449352d5723c22867836bb Mon Sep 17 00:00:00 2001 From: Nourhan Shata Date: Thu, 21 Aug 2025 12:34:10 +0200 Subject: [PATCH 16/17] Reverting --- .../registry/model/PromptTemplateSubstitutionResponse.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/model/PromptTemplateSubstitutionResponse.java b/core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/model/PromptTemplateSubstitutionResponse.java index aa4587791..51be802a7 100644 --- a/core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/model/PromptTemplateSubstitutionResponse.java +++ b/core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/model/PromptTemplateSubstitutionResponse.java @@ -40,7 +40,7 @@ public class PromptTemplateSubstitutionResponse private final Map cloudSdkCustomFields = new LinkedHashMap<>(); /** Default constructor for PromptTemplateSubstitutionResponse. */ - public PromptTemplateSubstitutionResponse() {} + protected PromptTemplateSubstitutionResponse() {} /** * Set the parsedPrompt of this {@link PromptTemplateSubstitutionResponse} instance and return the From d8cca8b6729422dd8eaf9056f00f3c33fbe19c0e Mon Sep 17 00:00:00 2001 From: Nourhan Shata Date: Thu, 21 Aug 2025 13:18:11 +0200 Subject: [PATCH 17/17] Updating Releasing Notes --- docs/release_notes.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/release_notes.md b/docs/release_notes.md index f872d0927..6d043220b 100644 --- a/docs/release_notes.md +++ b/docs/release_notes.md @@ -25,6 +25,8 @@ - [Orchestration] Deprecated `OrchestrationAiModel.IBM_GRANITE_13B_CHAT` with no replacement. - [OpenAI] [Introduced SpringAI integration with our OpenAI client.](https://sap.github.io/ai-sdk/docs/java/spring-ai/openai) - 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` ### 📈 Improvements