From f561b575b7fcd407a44256b46edd924083c26e59 Mon Sep 17 00:00:00 2001 From: Vladimir Aseev Date: Mon, 14 Sep 2026 17:11:13 +0200 Subject: [PATCH 01/10] rework Prompt and OrchestrationConfig clients --- core-services/prompt-registry/pom.xml | 14 ++- ...ent.java => OrchestrationConfigMixin.java} | 32 +------ ...omptClient.java => PromptClientMixin.java} | 35 ++------ .../prompt/registry/PromptRegistryClient.java | 58 ++++++++++++ .../OrchestrationConfigClientTest.java | 6 +- .../registry/PromptRegistryClientTest.java | 56 +++++++++--- .../spring/SpringAiConverterTest.java | 90 ++++++++++--------- docs/release_notes.md | 24 +++++ .../controllers/PromptRegistryController.java | 54 +++++------ .../app/services/OrchestrationService.java | 16 ++-- 10 files changed, 235 insertions(+), 150 deletions(-) rename core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/{OrchestrationConfigClient.java => OrchestrationConfigMixin.java} (68%) rename core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/{PromptClient.java => PromptClientMixin.java} (79%) create mode 100644 core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/PromptRegistryClient.java diff --git a/core-services/prompt-registry/pom.xml b/core-services/prompt-registry/pom.xml index e7880ee9d..b07ec1b5d 100644 --- a/core-services/prompt-registry/pom.xml +++ b/core-services/prompt-registry/pom.xml @@ -73,10 +73,6 @@ com.fasterxml.jackson.core jackson-core - - com.google.guava - guava - com.fasterxml.jackson.core jackson-annotations @@ -107,6 +103,16 @@ assertj-core test + + com.sap.cloud.environment.servicebinding.api + java-access-api + test + + + com.sap.cloud.environment.servicebinding.api + java-core-api + test + diff --git a/core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/OrchestrationConfigClient.java b/core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/OrchestrationConfigMixin.java similarity index 68% rename from core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/OrchestrationConfigClient.java rename to core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/OrchestrationConfigMixin.java index 5a9d1096b..ebbc56d65 100644 --- a/core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/OrchestrationConfigClient.java +++ b/core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/OrchestrationConfigMixin.java @@ -4,9 +4,7 @@ import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.google.common.annotations.Beta; import com.sap.ai.sdk.core.AiCoreService; -import com.sap.ai.sdk.prompt.registry.client.OrchestrationConfigsApi; import com.sap.ai.sdk.prompt.registry.model.AzureContentSafetyInputFilterConfig; import com.sap.ai.sdk.prompt.registry.model.AzureContentSafetyOutputFilterConfig; import com.sap.ai.sdk.prompt.registry.model.InputFilterConfig; @@ -18,32 +16,10 @@ import lombok.NoArgsConstructor; import lombok.val; -/** - * Client for managing Orchestration Configurations in the Prompt Registry service. - * - * @since 1.15.0 - */ -@Beta -public class OrchestrationConfigClient extends OrchestrationConfigsApi { - - /** - * Instantiates a client to manage Orchestration Configurations on the Prompt Registry service. - */ - public OrchestrationConfigClient() { - this(new AiCoreService()); - } - - /** - * Instantiates a client to manage Orchestration Configurations on the Prompt Registry service. - * - * @param aiCoreService The configured connectivity instance to AI Core - */ - public OrchestrationConfigClient(@Nonnull final AiCoreService aiCoreService) { - super(addMixin(aiCoreService)); - } +class OrchestrationConfigMixin { @Nonnull - private static ApiClient addMixin(@Nonnull final AiCoreService service) { + static ApiClient addMixin(@Nonnull final AiCoreService service) { final var destination = service.getBaseDestination(); val objectMapper = @@ -54,8 +30,8 @@ private static ApiClient addMixin(@Nonnull final AiCoreService service) { return ApiClient.create(destination).withObjectMapper(objectMapper); } - @NoArgsConstructor(access = AccessLevel.PRIVATE) - private static class JacksonMixin { + @NoArgsConstructor(access = AccessLevel.PACKAGE) + static class JacksonMixin { @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, diff --git a/core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/PromptClient.java b/core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/PromptClientMixin.java similarity index 79% rename from core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/PromptClient.java rename to core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/PromptClientMixin.java index ae252897a..6d2f8652e 100644 --- a/core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/PromptClient.java +++ b/core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/PromptClientMixin.java @@ -11,7 +11,6 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.sap.ai.sdk.core.AiCoreService; -import com.sap.ai.sdk.prompt.registry.client.PromptTemplatesApi; import com.sap.ai.sdk.prompt.registry.model.MultiChatContent; import com.sap.ai.sdk.prompt.registry.model.MultiChatTemplate; import com.sap.ai.sdk.prompt.registry.model.PromptTemplate; @@ -28,34 +27,10 @@ import lombok.NoArgsConstructor; import lombok.val; -/** - * Client for the Prompt Registry service. - * - * @since 1.6.0 - */ -public class PromptClient extends PromptTemplatesApi { - - /** - * Instantiates this a client to invoke operations on the Prompt Registry service. - * - * @since 1.6.0 - */ - public PromptClient() { - this(new AiCoreService()); - } - - /** - * Instantiates this a client to invoke operations on the Prompt Registry service. - * - * @param aiCoreService The configured connectivity instance to AI Core - * @since 1.6.0 - */ - public PromptClient(@Nonnull final AiCoreService aiCoreService) { - super(addMixin(aiCoreService)); - } +class PromptClientMixin { @Nonnull - private static ApiClient addMixin(@Nonnull final AiCoreService service) { + static ApiClient addMixin(@Nonnull final AiCoreService service) { final var destination = service.getBaseDestination(); val objectMapper = @@ -66,8 +41,8 @@ private static ApiClient addMixin(@Nonnull final AiCoreService service) { return ApiClient.create(destination).withObjectMapper(objectMapper); } - @NoArgsConstructor(access = AccessLevel.PRIVATE) - private static class JacksonMixin { + @NoArgsConstructor(access = AccessLevel.PACKAGE) + static class JacksonMixin { @JsonTypeInfo(use = JsonTypeInfo.Id.NONE) @JsonDeserialize(using = PromptTemplateDeserializer.class) interface TemplateMixIn {} @@ -85,7 +60,7 @@ interface TemplateMixIn {} interface ResponseFormat {} } - private static class PromptTemplateDeserializer extends JsonDeserializer { + static class PromptTemplateDeserializer extends JsonDeserializer { @Override public PromptTemplate deserialize( diff --git a/core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/PromptRegistryClient.java b/core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/PromptRegistryClient.java new file mode 100644 index 000000000..86c661dcd --- /dev/null +++ b/core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/PromptRegistryClient.java @@ -0,0 +1,58 @@ +package com.sap.ai.sdk.prompt.registry; + +import com.sap.ai.sdk.core.AiCoreService; +import com.sap.ai.sdk.prompt.registry.client.OrchestrationConfigsApi; +import com.sap.ai.sdk.prompt.registry.client.PromptTemplatesApi; +import javax.annotation.Nonnull; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; + +/** + * Unified client to use Prompt Registry API + * + * @since 2.0 + */ +@AllArgsConstructor(access = AccessLevel.PACKAGE) +public class PromptRegistryClient { + + private final PromptTemplatesApi promptTemplatesApi; + private final OrchestrationConfigsApi orchestrationConfigsApi; + + /** Constructs default PromptRegistryClient */ + public PromptRegistryClient() { + promptTemplatesApi = new PromptTemplatesApi(PromptClientMixin.addMixin(new AiCoreService())); + orchestrationConfigsApi = + new OrchestrationConfigsApi(OrchestrationConfigMixin.addMixin(new AiCoreService())); + } + + /** + * Constructs PromptRegistryClient with customized AiCoreService + * + * @param service customized AiCoreService + */ + public PromptRegistryClient(@Nonnull final AiCoreService service) { + promptTemplatesApi = new PromptTemplatesApi(PromptClientMixin.addMixin(service)); + orchestrationConfigsApi = + new OrchestrationConfigsApi(OrchestrationConfigMixin.addMixin(service)); + } + + /** + * Provides caller with PromptTemplatesAPI client + * + * @return the client + */ + @Nonnull + public PromptTemplatesApi prompt() { + return promptTemplatesApi; + } + + /** + * Provides caller with OrchestrationConfigsAPI client + * + * @return the client + */ + @Nonnull + public OrchestrationConfigsApi orchestration() { + return orchestrationConfigsApi; + } +} diff --git a/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/OrchestrationConfigClientTest.java b/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/OrchestrationConfigClientTest.java index 2365d0bdc..6f87d7498 100644 --- a/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/OrchestrationConfigClientTest.java +++ b/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/OrchestrationConfigClientTest.java @@ -17,18 +17,18 @@ public class OrchestrationConfigClientTest { private static final WireMockExtension WM = WireMockExtension.newInstance().options(wireMockConfig().dynamicPort()).build(); - private static OrchestrationConfigClient client; + private static PromptRegistryClient client; @BeforeEach void setup() { final HttpDestination destination = DefaultHttpDestination.builder(WM.baseUrl()).build(); final AiCoreService service = new AiCoreService().withBaseDestination(destination); - client = new OrchestrationConfigClient(service); + client = new PromptRegistryClient(service); } @Test void testPipelines() { - final var result = client.listOrchestrationConfigs(); + final var result = client.orchestration().listOrchestrationConfigs(); assertThat(result.getCount()).isEqualTo(2); assertThat(result.getResources()).hasSize(2); final var template = result.getResources().get(0); diff --git a/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/PromptRegistryClientTest.java b/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/PromptRegistryClientTest.java index b81131c0a..c66d22ae2 100644 --- a/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/PromptRegistryClientTest.java +++ b/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/PromptRegistryClientTest.java @@ -2,6 +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.assertThatCode; import static org.assertj.core.api.Assertions.assertThatThrownBy; import com.github.tomakehurst.wiremock.junit5.WireMockExtension; @@ -14,10 +15,16 @@ import com.sap.ai.sdk.prompt.registry.model.ResponseFormatText; import com.sap.ai.sdk.prompt.registry.model.SingleChatTemplate; import com.sap.ai.sdk.prompt.registry.model.TextContent; +import com.sap.cloud.environment.servicebinding.api.DefaultServiceBindingAccessor; +import com.sap.cloud.environment.servicebinding.api.DefaultServiceBindingBuilder; +import com.sap.cloud.environment.servicebinding.api.ServiceBindingAccessor; +import com.sap.cloud.environment.servicebinding.api.ServiceIdentifier; 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 java.util.UUID; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -27,18 +34,43 @@ class PromptRegistryClientTest { private static final WireMockExtension WM = WireMockExtension.newInstance().options(wireMockConfig().dynamicPort()).build(); - private static PromptClient client; + private static PromptRegistryClient client; + private ServiceBindingAccessor originalAccessor; @BeforeEach void setup() { + originalAccessor = DefaultServiceBindingAccessor.getInstance(); final HttpDestination destination = DefaultHttpDestination.builder(WM.baseUrl()).build(); final AiCoreService service = new AiCoreService().withBaseDestination(destination); - client = new PromptClient(service); + client = new PromptRegistryClient(service); + } + + @AfterEach + void teardown() { + DefaultServiceBindingAccessor.setInstance(originalAccessor); + } + + @Test + void testDefaultConstructor() { + final var binding = + new DefaultServiceBindingBuilder() + .withServiceIdentifier(ServiceIdentifier.AI_CORE) + .withCredentials( + Map.of( + "clientid", "client-id", + "clientsecret", "client-secret", + "credential-type", "binding-secret", + "url", WM.baseUrl(), + "serviceurls", Map.of("AI_API_URL", WM.baseUrl()))) + .build(); + DefaultServiceBindingAccessor.setInstance(() -> List.of(binding)); + + assertThatCode(PromptRegistryClient::new).doesNotThrowAnyException(); } @Test void testPipelines() { - final var result = client.listPromptTemplates(); + final var result = client.prompt().listPromptTemplates(); assertThat(result.getCount()).isEqualTo(2); assertThat(result.getResources()).hasSize(2); final var template = result.getResources().get(0); @@ -55,7 +87,7 @@ void testPipelines() { @Test void testGetTemplateWithResponseFormatText() { final var uuid = UUID.fromString("22117a64-9f2c-481b-9402-8acb66eeb707"); - final PromptTemplateGetResponse response = client.getPromptTemplateByUuid(uuid); + final PromptTemplateGetResponse response = client.prompt().getPromptTemplateByUuid(uuid); assertThat(response.getName()).isEqualTo("test"); assertThat(response.getVersion()).isEqualTo("0.0.1"); @@ -70,7 +102,7 @@ void testGetTemplateWithResponseFormatText() { @Test void testGetTemplateWithResponseFormatJsonObject() { final var uuid = UUID.fromString("21cb1358-0bf1-4f43-870b-00f14d0f9f16"); - final var response = client.getPromptTemplateByUuid(uuid); + final var response = client.prompt().getPromptTemplateByUuid(uuid); assertThat(response.getName()).isEqualTo("test"); assertThat(response.getVersion()).isEqualTo("0.0.1"); @@ -85,7 +117,7 @@ void testGetTemplateWithResponseFormatJsonObject() { @Test void testGetTemplateWithResponseFormatJsonSchema() { final var uuid = UUID.fromString("0f79fec4-ae07-4c35-96e3-df7f4a3f1df5"); - final var response = client.getPromptTemplateByUuid(uuid); + final var response = client.prompt().getPromptTemplateByUuid(uuid); assertThat(response.getName()).isEqualTo("test"); assertThat(response.getVersion()).isEqualTo("0.0.1"); @@ -105,7 +137,7 @@ void testGetTemplateWithResponseFormatJsonSchema() { @Test void testGetTemplateWithMultiChatTemplate() { final var uuid = UUID.fromString("8f79fec4-ae07-4c35-96e3-df7f4a3f1df5"); - final var response = client.getPromptTemplateByUuid(uuid); + final var response = client.prompt().getPromptTemplateByUuid(uuid); assertThat(response.getSpec()).isNotNull(); assertThat(response.getSpec().getTemplate()).hasSize(2); @@ -126,7 +158,7 @@ void testGetTemplateWithMultiChatTemplate() { void testGetTemplateWithInvalidRoleType() { final var uuid = UUID.fromString("45cb1358-0bf1-4f43-870b-00f14d0f9f16"); - assertThatThrownBy(() -> client.getPromptTemplateByUuid(uuid)) + assertThatThrownBy(() -> client.prompt().getPromptTemplateByUuid(uuid)) .hasStackTraceContaining("PromptTemplate requires textual 'role' property."); } @@ -134,7 +166,7 @@ void testGetTemplateWithInvalidRoleType() { void testGetTemplateWithInvalidContentType() { final var uuid = UUID.fromString("55cb1358-0bf1-4f43-870b-00f14d0f9f16"); - assertThatThrownBy(() -> client.getPromptTemplateByUuid(uuid)) + assertThatThrownBy(() -> client.prompt().getPromptTemplateByUuid(uuid)) .hasStackTraceContaining( "PromptTemplate content must be either a string or an array, but found: BOOLEAN"); } @@ -146,8 +178,10 @@ void testParsePromptTemplateHotPath() { .inputParams(Map.of("inputExample", "I love football")); final var response = - client.parsePromptTemplateByNameVersion( - "categorization", "0.0.1", "hotpath-serde", "default", null, false, request); + client + .prompt() + .parsePromptTemplateByNameVersion( + "categorization", "0.0.1", "hotpath-serde", "default", null, false, request); assertThat(response.getParsedPrompt()).hasSize(2); assertThat(response.getParsedPrompt().get(0)).isInstanceOf(SingleChatTemplate.class); 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 7008efabe..a5f642731 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 @@ -6,7 +6,7 @@ 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.PromptRegistryClient; import com.sap.ai.sdk.prompt.registry.model.MultiChatContent; import com.sap.ai.sdk.prompt.registry.model.MultiChatTemplate; import com.sap.ai.sdk.prompt.registry.model.PromptTemplate; @@ -33,17 +33,19 @@ public class SpringAiConverterTest { @Test void testPromptRegistryToSpringAi() { - var client = new PromptClient(SERVICE); + var client = new PromptRegistryClient(SERVICE); val promptResponse = - client.parsePromptTemplateByNameVersion( - "categorization", - "0.0.1", - "java-e2e-test", - "default", - null, - false, - PromptTemplateSubstitutionRequest.create() - .inputParams(Map.of("inputExample", "I love football"))); + client + .prompt() + .parsePromptTemplateByNameVersion( + "categorization", + "0.0.1", + "java-e2e-test", + "default", + null, + false, + PromptTemplateSubstitutionRequest.create() + .inputParams(Map.of("inputExample", "I love football"))); List messages = SpringAiConverter.promptTemplateToMessages(promptResponse); assertThat(messages) @@ -56,17 +58,19 @@ void testPromptRegistryToSpringAi() { @Test void testInvalidRoleThrowsException() { - var client = new PromptClient(SERVICE); + var client = new PromptRegistryClient(SERVICE); val errorPrompt = - client.parsePromptTemplateByNameVersion( - "categorization", - "0.0.1", - "error", - "default", - null, - false, - PromptTemplateSubstitutionRequest.create() - .inputParams(Map.of("inputExample", "I love football"))); + client + .prompt() + .parsePromptTemplateByNameVersion( + "categorization", + "0.0.1", + "error", + "default", + null, + false, + PromptTemplateSubstitutionRequest.create() + .inputParams(Map.of("inputExample", "I love football"))); assertThatThrownBy(() -> SpringAiConverter.promptTemplateToMessages(errorPrompt)) .isInstanceOf(IllegalArgumentException.class) @@ -75,17 +79,19 @@ void testInvalidRoleThrowsException() { @Test void testMultiChatTemplateTextContentToSpringAi() { - var client = new PromptClient(SERVICE); + var client = new PromptRegistryClient(SERVICE); val promptResponse = - client.parsePromptTemplateByNameVersion( - "categorization", - "0.0.1", - "multi-text", - "default", - null, - false, - PromptTemplateSubstitutionRequest.create() - .inputParams(Map.of("inputExample", "I love football"))); + client + .prompt() + .parsePromptTemplateByNameVersion( + "categorization", + "0.0.1", + "multi-text", + "default", + null, + false, + PromptTemplateSubstitutionRequest.create() + .inputParams(Map.of("inputExample", "I love football"))); List messages = SpringAiConverter.promptTemplateToMessages(promptResponse); assertThat(messages) @@ -94,17 +100,19 @@ void testMultiChatTemplateTextContentToSpringAi() { @Test void testMultiChatTemplateImageContentThrowsException() { - var client = new PromptClient(SERVICE); + var client = new PromptRegistryClient(SERVICE); val promptResponse = - client.parsePromptTemplateByNameVersion( - "categorization", - "0.0.1", - "multi-image", - "default", - null, - false, - PromptTemplateSubstitutionRequest.create() - .inputParams(Map.of("inputExample", "I love football"))); + client + .prompt() + .parsePromptTemplateByNameVersion( + "categorization", + "0.0.1", + "multi-image", + "default", + null, + false, + PromptTemplateSubstitutionRequest.create() + .inputParams(Map.of("inputExample", "I love football"))); assertThatThrownBy(() -> SpringAiConverter.promptTemplateToMessages(promptResponse)) .isInstanceOf(UnsupportedOperationException.class) diff --git a/docs/release_notes.md b/docs/release_notes.md index c8381b375..7d80ec8af 100644 --- a/docs/release_notes.md +++ b/docs/release_notes.md @@ -10,6 +10,30 @@ - [RPT] SAP-RPT was updated to the newer 1.6.0 API - [Orchestration] Spring AI support was upgraded to version `2.0.1` +- [Core] OrchestrationConfigClient and PromptClient were reworked into unified PromptRegistryClient, + see migration guide below for additional information + +#### Prompt registry client Migration Guide + +OrchestrationConfigClient and PromptClient were replaced with unified PromptRegistryClient, + +OrchestrationConfigClient: +```diff +-var orchestrationConfigClient = new OrchestrationConfigClient(); +-var configs = orchestrationConfigClient.listOrchestrationConfigs(); + ++var promptRegistryClient = new PromptRegistryClient(); ++var configs = promptRegistryClient.orchestration().listOrchestrationConfigs(); +``` + +PromptRegistryClient: +```diff +-var promptClient = new PromptClient(); +-var templates = promptClient.listPromptTemplates(); + ++var promptRegistryClient = new PromptRegistryClient(); ++var templates = promptRegistryClient.prompt().listPromptTemplates(); +``` #### Spring AI 2.0.1 Migration Guide 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 097a7c193..fa0b0e81f 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 @@ -3,8 +3,7 @@ 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.OrchestrationConfigClient; -import com.sap.ai.sdk.prompt.registry.PromptClient; +import com.sap.ai.sdk.prompt.registry.PromptRegistryClient; import com.sap.ai.sdk.prompt.registry.model.LLMModelDetails; import com.sap.ai.sdk.prompt.registry.model.OrchestrationConfigDeleteResponse; import com.sap.ai.sdk.prompt.registry.model.OrchestrationConfigListResponse; @@ -49,24 +48,25 @@ @RequestMapping("/prompt-registry") class PromptRegistryController { static final String NAME = "java-e2e-test"; - private static final PromptClient promptClient = new PromptClient(); - private static final OrchestrationConfigClient orchConfigClient = new OrchestrationConfigClient(); + private static final PromptRegistryClient unifiedClient = new PromptRegistryClient(); @GetMapping("/listTemplates") PromptTemplateListResponse listTemplates() { - return promptClient.listPromptTemplates(); + return unifiedClient.prompt().listPromptTemplates(); } @GetMapping("/createTemplate") PromptTemplatePostResponse createTemplate() { - return promptClient.createUpdatePromptTemplate(getTemplate("Finance, Tech, Sports")); + return unifiedClient.prompt().createUpdatePromptTemplate(getTemplate("Finance, Tech, Sports")); } @GetMapping("/updateTemplate") PromptTemplatePostResponse updateTemplate() { // create template then update - promptClient.createUpdatePromptTemplate(getTemplate("Finance, Tech, Sports")); - return promptClient.createUpdatePromptTemplate(getTemplate("Finance, Tech, Sports, Politics")); + unifiedClient.prompt().createUpdatePromptTemplate(getTemplate("Finance, Tech, Sports")); + return unifiedClient + .prompt() + .createUpdatePromptTemplate(getTemplate("Finance, Tech, Sports, Politics")); } private PromptTemplatePostRequest getTemplate(final String categories) { @@ -89,40 +89,42 @@ private PromptTemplatePostRequest getTemplate(final String categories) { @GetMapping("/history") PromptTemplateListResponse history() { - return promptClient.listPromptTemplateHistory("categorization", "0.0.1", NAME); + return unifiedClient.prompt().listPromptTemplateHistory("categorization", "0.0.1", NAME); } @GetMapping("/importTemplate") PromptTemplatePostResponse importTemplate() throws IOException { val template = new ClassPathResource("prompt-template.yaml").getFile(); - return promptClient.importPromptTemplate("default", null, template); + return unifiedClient.prompt().importPromptTemplate("default", null, template); } @GetMapping("/exportTemplate") byte[] exportTemplate() throws IOException { final var template = importTemplate(); - return promptClient.exportPromptTemplate(template.getId()); + return unifiedClient.prompt().exportPromptTemplate(template.getId()); } @GetMapping("/useTemplate") PromptTemplateSubstitutionResponse useTemplate() { final var template = createTemplate(); - return promptClient.parsePromptTemplateById( - template.getId(), - "default", - null, - false, - PromptTemplateSubstitutionRequest.create() - .inputParams(Map.of("inputExample", "I love football"))); + return unifiedClient + .prompt() + .parsePromptTemplateById( + template.getId(), + "default", + null, + false, + PromptTemplateSubstitutionRequest.create() + .inputParams(Map.of("inputExample", "I love football"))); } @GetMapping("/deleteTemplate") List deleteTemplate() { - final PromptTemplateListResponse templates = promptClient.listPromptTemplates(); + final PromptTemplateListResponse templates = unifiedClient.prompt().listPromptTemplates(); return templates.getResources().stream() .filter(template -> NAME.equals(template.getName())) - .map(template -> promptClient.deletePromptTemplate(template.getId())) + .map(template -> unifiedClient.prompt().deletePromptTemplate(template.getId())) .toList(); } @@ -135,7 +137,8 @@ Generation promptRegistryToSpringAi() { val cl = ChatClient.builder(openAiClient).defaultAdvisors(advisor).build(); val promptResponse = - new PromptClient() + new PromptRegistryClient() + .prompt() .parsePromptTemplateByNameVersion( "categorization", "0.0.1", @@ -158,7 +161,7 @@ Generation promptRegistryToSpringAi() { @GetMapping("/listOrchConfigs") OrchestrationConfigListResponse listOrchConfigs() { - return orchConfigClient.listOrchestrationConfigs(); + return unifiedClient.orchestration().listOrchestrationConfigs(); } @GetMapping("/createOrchConfig") @@ -184,16 +187,17 @@ OrchestrationConfigPostResponse createOrchConfig() { .version("0.0.1") .scenario("sdk-test-scenario") .spec(orchestrationConfig); - return orchConfigClient.createUpdateOrchestrationConfig(postRequest); + return unifiedClient.orchestration().createUpdateOrchestrationConfig(postRequest); } @GetMapping("/deleteOrchConfig") List deleteOrchConfig() { - final OrchestrationConfigListResponse configs = orchConfigClient.listOrchestrationConfigs(); + final OrchestrationConfigListResponse configs = + unifiedClient.orchestration().listOrchestrationConfigs(); return configs.getResources().stream() .filter(config -> NAME.equals(config.getName())) - .map(config -> orchConfigClient.deleteOrchestrationConfig(config.getId())) + .map(config -> unifiedClient.orchestration().deleteOrchestrationConfig(config.getId())) .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 088cfab5f..afa6954bf 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 @@ -46,7 +46,7 @@ import com.sap.ai.sdk.orchestration.model.SearchDocumentKeyValueListPair; import com.sap.ai.sdk.orchestration.model.SearchSelectOptionEnum; import com.sap.ai.sdk.orchestration.model.Template; -import com.sap.ai.sdk.prompt.registry.OrchestrationConfigClient; +import com.sap.ai.sdk.prompt.registry.PromptRegistryClient; import com.sap.ai.sdk.prompt.registry.model.LLMModelDetails; import com.sap.ai.sdk.prompt.registry.model.OrchestrationConfigPostRequest; import com.sap.ai.sdk.prompt.registry.model.PartialModuleConfigs; @@ -888,21 +888,21 @@ public OrchestrationChatResponse executeConfigFromReference() { } private void ensureOrchestrationConfigExists(final String scenario, final String name) { - final OrchestrationConfigClient orchConfigClient = new OrchestrationConfigClient(); - if (!orchConfigExists("test-config-for-OrchestrationTest", orchConfigClient)) { + final PromptRegistryClient unifiedClient = new PromptRegistryClient(); + if (!orchConfigExists("test-config-for-OrchestrationTest", unifiedClient)) { final OrchestrationConfigPostRequest postRequest = OrchestrationConfigPostRequest.create() .name(name) .version("0.0.1") .scenario(scenario) .spec(buildOrchestrationConfig()); - orchConfigClient.createUpdateOrchestrationConfig(postRequest); + unifiedClient.orchestration().createUpdateOrchestrationConfig(postRequest); } } private boolean orchConfigExists( - final String configName, final OrchestrationConfigClient orchConfigClient) { - return orchConfigClient.listOrchestrationConfigs().getResources().stream() + final String configName, final PromptRegistryClient unifiedClient) { + return unifiedClient.orchestration().listOrchestrationConfigs().getResources().stream() .anyMatch(resp -> resp.getName().equals(configName)); } @@ -975,9 +975,9 @@ public Stream streamDeltasWithInlineConfig( public Stream streamDeltasWithReferenceById() { // get a valid id ensureOrchestrationConfigExists("sdk-test-paraphrase", "create-3-paraphrases-of-sentence"); - val orchConfigClient = new OrchestrationConfigClient(); + val unifiedClient = new PromptRegistryClient(); val id = - orchConfigClient.listOrchestrationConfigs().getResources().stream() + unifiedClient.orchestration().listOrchestrationConfigs().getResources().stream() .filter(r -> r.getName().equals("test-config-for-OrchestrationTest")) .findFirst() .orElseThrow() From 08cfd2838aaac7b650c9ce1332253bc3cc0ba0e7 Mon Sep 17 00:00:00 2001 From: Vladimir Aseev Date: Mon, 14 Sep 2026 17:25:19 +0200 Subject: [PATCH 02/10] create AiCoreService in PromptRegistryClient only once --- .../com/sap/ai/sdk/prompt/registry/PromptRegistryClient.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/PromptRegistryClient.java b/core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/PromptRegistryClient.java index 86c661dcd..b51560b87 100644 --- a/core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/PromptRegistryClient.java +++ b/core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/PromptRegistryClient.java @@ -20,9 +20,10 @@ public class PromptRegistryClient { /** Constructs default PromptRegistryClient */ public PromptRegistryClient() { - promptTemplatesApi = new PromptTemplatesApi(PromptClientMixin.addMixin(new AiCoreService())); + final var aiCoreService = new AiCoreService(); + promptTemplatesApi = new PromptTemplatesApi(PromptClientMixin.addMixin(aiCoreService)); orchestrationConfigsApi = - new OrchestrationConfigsApi(OrchestrationConfigMixin.addMixin(new AiCoreService())); + new OrchestrationConfigsApi(OrchestrationConfigMixin.addMixin(aiCoreService)); } /** From eb3f68a6161c12d7c1bacbf3be271ab194dfed56 Mon Sep 17 00:00:00 2001 From: Vladimir Aseev Date: Tue, 15 Sep 2026 10:16:36 +0200 Subject: [PATCH 03/10] address pr feedback --- .../prompt/registry/PromptRegistryClient.java | 23 ++++++------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/PromptRegistryClient.java b/core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/PromptRegistryClient.java index b51560b87..88541d9df 100644 --- a/core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/PromptRegistryClient.java +++ b/core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/PromptRegistryClient.java @@ -4,26 +4,19 @@ import com.sap.ai.sdk.prompt.registry.client.OrchestrationConfigsApi; import com.sap.ai.sdk.prompt.registry.client.PromptTemplatesApi; import javax.annotation.Nonnull; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; /** * Unified client to use Prompt Registry API * * @since 2.0 */ -@AllArgsConstructor(access = AccessLevel.PACKAGE) public class PromptRegistryClient { - private final PromptTemplatesApi promptTemplatesApi; - private final OrchestrationConfigsApi orchestrationConfigsApi; + private final AiCoreService aiCoreService; /** Constructs default PromptRegistryClient */ public PromptRegistryClient() { - final var aiCoreService = new AiCoreService(); - promptTemplatesApi = new PromptTemplatesApi(PromptClientMixin.addMixin(aiCoreService)); - orchestrationConfigsApi = - new OrchestrationConfigsApi(OrchestrationConfigMixin.addMixin(aiCoreService)); + this(new AiCoreService()); } /** @@ -32,28 +25,26 @@ public PromptRegistryClient() { * @param service customized AiCoreService */ public PromptRegistryClient(@Nonnull final AiCoreService service) { - promptTemplatesApi = new PromptTemplatesApi(PromptClientMixin.addMixin(service)); - orchestrationConfigsApi = - new OrchestrationConfigsApi(OrchestrationConfigMixin.addMixin(service)); + aiCoreService = service; } /** - * Provides caller with PromptTemplatesAPI client + * Get the prompt templates client * * @return the client */ @Nonnull public PromptTemplatesApi prompt() { - return promptTemplatesApi; + return new PromptTemplatesApi(PromptClientMixin.addMixin(aiCoreService)); } /** - * Provides caller with OrchestrationConfigsAPI client + * Get the orchestration configs client * * @return the client */ @Nonnull public OrchestrationConfigsApi orchestration() { - return orchestrationConfigsApi; + return new OrchestrationConfigsApi(OrchestrationConfigMixin.addMixin(aiCoreService)); } } From 2bab710e51f7be256e102a0e152894e076555e6c Mon Sep 17 00:00:00 2001 From: Vladimir Aseev Date: Wed, 16 Sep 2026 12:40:55 +0200 Subject: [PATCH 04/10] polish code style --- .../prompt/registry/PromptRegistryClient.java | 2 +- .../OrchestrationConfigClientTest.java | 14 ++--- .../registry/PromptRegistryClientTest.java | 34 ++++++------ docs/release_notes.md | 2 +- .../controllers/PromptRegistryController.java | 52 ++++++++++--------- .../app/services/OrchestrationService.java | 6 +-- 6 files changed, 57 insertions(+), 53 deletions(-) diff --git a/core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/PromptRegistryClient.java b/core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/PromptRegistryClient.java index 88541d9df..bbf58d0fe 100644 --- a/core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/PromptRegistryClient.java +++ b/core-services/prompt-registry/src/main/java/com/sap/ai/sdk/prompt/registry/PromptRegistryClient.java @@ -44,7 +44,7 @@ public PromptTemplatesApi prompt() { * @return the client */ @Nonnull - public OrchestrationConfigsApi orchestration() { + public OrchestrationConfigsApi orchestrationConfig() { return new OrchestrationConfigsApi(OrchestrationConfigMixin.addMixin(aiCoreService)); } } diff --git a/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/OrchestrationConfigClientTest.java b/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/OrchestrationConfigClientTest.java index 6f87d7498..ccbbb5c2d 100644 --- a/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/OrchestrationConfigClientTest.java +++ b/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/OrchestrationConfigClientTest.java @@ -5,10 +5,11 @@ import com.github.tomakehurst.wiremock.junit5.WireMockExtension; import com.sap.ai.sdk.core.AiCoreService; +import com.sap.ai.sdk.prompt.registry.client.OrchestrationConfigsApi; import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination; import com.sap.cloud.sdk.cloudplatform.connectivity.HttpDestination; import java.util.UUID; -import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -17,18 +18,19 @@ public class OrchestrationConfigClientTest { private static final WireMockExtension WM = WireMockExtension.newInstance().options(wireMockConfig().dynamicPort()).build(); - private static PromptRegistryClient client; + private static OrchestrationConfigsApi orchestrationConfigsClient; - @BeforeEach - void setup() { + @BeforeAll + static void setup() { final HttpDestination destination = DefaultHttpDestination.builder(WM.baseUrl()).build(); final AiCoreService service = new AiCoreService().withBaseDestination(destination); - client = new PromptRegistryClient(service); + final var client = new PromptRegistryClient(service); + orchestrationConfigsClient = client.orchestrationConfig(); } @Test void testPipelines() { - final var result = client.orchestration().listOrchestrationConfigs(); + final var result = orchestrationConfigsClient.listOrchestrationConfigs(); assertThat(result.getCount()).isEqualTo(2); assertThat(result.getResources()).hasSize(2); final var template = result.getResources().get(0); diff --git a/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/PromptRegistryClientTest.java b/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/PromptRegistryClientTest.java index c66d22ae2..d33ecdd4b 100644 --- a/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/PromptRegistryClientTest.java +++ b/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/PromptRegistryClientTest.java @@ -7,6 +7,7 @@ import com.github.tomakehurst.wiremock.junit5.WireMockExtension; import com.sap.ai.sdk.core.AiCoreService; +import com.sap.ai.sdk.prompt.registry.client.PromptTemplatesApi; import com.sap.ai.sdk.prompt.registry.model.MultiChatTemplate; import com.sap.ai.sdk.prompt.registry.model.PromptTemplateGetResponse; import com.sap.ai.sdk.prompt.registry.model.PromptTemplateSubstitutionRequest; @@ -25,7 +26,7 @@ import java.util.Map; import java.util.UUID; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -34,15 +35,16 @@ class PromptRegistryClientTest { private static final WireMockExtension WM = WireMockExtension.newInstance().options(wireMockConfig().dynamicPort()).build(); - private static PromptRegistryClient client; - private ServiceBindingAccessor originalAccessor; + private static PromptTemplatesApi promptClient; + private static ServiceBindingAccessor originalAccessor; - @BeforeEach - void setup() { + @BeforeAll + static void setup() { originalAccessor = DefaultServiceBindingAccessor.getInstance(); final HttpDestination destination = DefaultHttpDestination.builder(WM.baseUrl()).build(); final AiCoreService service = new AiCoreService().withBaseDestination(destination); - client = new PromptRegistryClient(service); + PromptRegistryClient client = new PromptRegistryClient(service); + promptClient = client.prompt(); } @AfterEach @@ -70,7 +72,7 @@ void testDefaultConstructor() { @Test void testPipelines() { - final var result = client.prompt().listPromptTemplates(); + final var result = promptClient.listPromptTemplates(); assertThat(result.getCount()).isEqualTo(2); assertThat(result.getResources()).hasSize(2); final var template = result.getResources().get(0); @@ -87,7 +89,7 @@ void testPipelines() { @Test void testGetTemplateWithResponseFormatText() { final var uuid = UUID.fromString("22117a64-9f2c-481b-9402-8acb66eeb707"); - final PromptTemplateGetResponse response = client.prompt().getPromptTemplateByUuid(uuid); + final PromptTemplateGetResponse response = promptClient.getPromptTemplateByUuid(uuid); assertThat(response.getName()).isEqualTo("test"); assertThat(response.getVersion()).isEqualTo("0.0.1"); @@ -102,7 +104,7 @@ void testGetTemplateWithResponseFormatText() { @Test void testGetTemplateWithResponseFormatJsonObject() { final var uuid = UUID.fromString("21cb1358-0bf1-4f43-870b-00f14d0f9f16"); - final var response = client.prompt().getPromptTemplateByUuid(uuid); + final var response = promptClient.getPromptTemplateByUuid(uuid); assertThat(response.getName()).isEqualTo("test"); assertThat(response.getVersion()).isEqualTo("0.0.1"); @@ -117,7 +119,7 @@ void testGetTemplateWithResponseFormatJsonObject() { @Test void testGetTemplateWithResponseFormatJsonSchema() { final var uuid = UUID.fromString("0f79fec4-ae07-4c35-96e3-df7f4a3f1df5"); - final var response = client.prompt().getPromptTemplateByUuid(uuid); + final var response = promptClient.getPromptTemplateByUuid(uuid); assertThat(response.getName()).isEqualTo("test"); assertThat(response.getVersion()).isEqualTo("0.0.1"); @@ -137,7 +139,7 @@ void testGetTemplateWithResponseFormatJsonSchema() { @Test void testGetTemplateWithMultiChatTemplate() { final var uuid = UUID.fromString("8f79fec4-ae07-4c35-96e3-df7f4a3f1df5"); - final var response = client.prompt().getPromptTemplateByUuid(uuid); + final var response = promptClient.getPromptTemplateByUuid(uuid); assertThat(response.getSpec()).isNotNull(); assertThat(response.getSpec().getTemplate()).hasSize(2); @@ -158,7 +160,7 @@ void testGetTemplateWithMultiChatTemplate() { void testGetTemplateWithInvalidRoleType() { final var uuid = UUID.fromString("45cb1358-0bf1-4f43-870b-00f14d0f9f16"); - assertThatThrownBy(() -> client.prompt().getPromptTemplateByUuid(uuid)) + assertThatThrownBy(() -> promptClient.getPromptTemplateByUuid(uuid)) .hasStackTraceContaining("PromptTemplate requires textual 'role' property."); } @@ -166,7 +168,7 @@ void testGetTemplateWithInvalidRoleType() { void testGetTemplateWithInvalidContentType() { final var uuid = UUID.fromString("55cb1358-0bf1-4f43-870b-00f14d0f9f16"); - assertThatThrownBy(() -> client.prompt().getPromptTemplateByUuid(uuid)) + assertThatThrownBy(() -> promptClient.getPromptTemplateByUuid(uuid)) .hasStackTraceContaining( "PromptTemplate content must be either a string or an array, but found: BOOLEAN"); } @@ -178,10 +180,8 @@ void testParsePromptTemplateHotPath() { .inputParams(Map.of("inputExample", "I love football")); final var response = - client - .prompt() - .parsePromptTemplateByNameVersion( - "categorization", "0.0.1", "hotpath-serde", "default", null, false, request); + promptClient.parsePromptTemplateByNameVersion( + "categorization", "0.0.1", "hotpath-serde", "default", null, false, request); assertThat(response.getParsedPrompt()).hasSize(2); assertThat(response.getParsedPrompt().get(0)).isInstanceOf(SingleChatTemplate.class); diff --git a/docs/release_notes.md b/docs/release_notes.md index 7d80ec8af..d3833a6ef 100644 --- a/docs/release_notes.md +++ b/docs/release_notes.md @@ -23,7 +23,7 @@ OrchestrationConfigClient: -var configs = orchestrationConfigClient.listOrchestrationConfigs(); +var promptRegistryClient = new PromptRegistryClient(); -+var configs = promptRegistryClient.orchestration().listOrchestrationConfigs(); ++var configs = promptRegistryClient.orchestrationConfig().listOrchestrationConfigs(); ``` PromptRegistryClient: 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 fa0b0e81f..3638c9022 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 @@ -4,6 +4,8 @@ import com.sap.ai.sdk.foundationmodels.openai.OpenAiModel; import com.sap.ai.sdk.foundationmodels.openai.spring.OpenAiChatModel; import com.sap.ai.sdk.prompt.registry.PromptRegistryClient; +import com.sap.ai.sdk.prompt.registry.client.OrchestrationConfigsApi; +import com.sap.ai.sdk.prompt.registry.client.PromptTemplatesApi; import com.sap.ai.sdk.prompt.registry.model.LLMModelDetails; import com.sap.ai.sdk.prompt.registry.model.OrchestrationConfigDeleteResponse; import com.sap.ai.sdk.prompt.registry.model.OrchestrationConfigListResponse; @@ -48,25 +50,27 @@ @RequestMapping("/prompt-registry") class PromptRegistryController { static final String NAME = "java-e2e-test"; - private static final PromptRegistryClient unifiedClient = new PromptRegistryClient(); + private static final OrchestrationConfigsApi orchestrationConfigsClient = + new PromptRegistryClient().orchestrationConfig(); + private static final PromptTemplatesApi promptTemplatesClient = + new PromptRegistryClient().prompt(); @GetMapping("/listTemplates") PromptTemplateListResponse listTemplates() { - return unifiedClient.prompt().listPromptTemplates(); + return promptTemplatesClient.listPromptTemplates(); } @GetMapping("/createTemplate") PromptTemplatePostResponse createTemplate() { - return unifiedClient.prompt().createUpdatePromptTemplate(getTemplate("Finance, Tech, Sports")); + return promptTemplatesClient.createUpdatePromptTemplate(getTemplate("Finance, Tech, Sports")); } @GetMapping("/updateTemplate") PromptTemplatePostResponse updateTemplate() { // create template then update - unifiedClient.prompt().createUpdatePromptTemplate(getTemplate("Finance, Tech, Sports")); - return unifiedClient - .prompt() - .createUpdatePromptTemplate(getTemplate("Finance, Tech, Sports, Politics")); + promptTemplatesClient.createUpdatePromptTemplate(getTemplate("Finance, Tech, Sports")); + return promptTemplatesClient.createUpdatePromptTemplate( + getTemplate("Finance, Tech, Sports, Politics")); } private PromptTemplatePostRequest getTemplate(final String categories) { @@ -89,42 +93,40 @@ private PromptTemplatePostRequest getTemplate(final String categories) { @GetMapping("/history") PromptTemplateListResponse history() { - return unifiedClient.prompt().listPromptTemplateHistory("categorization", "0.0.1", NAME); + return promptTemplatesClient.listPromptTemplateHistory("categorization", "0.0.1", NAME); } @GetMapping("/importTemplate") PromptTemplatePostResponse importTemplate() throws IOException { val template = new ClassPathResource("prompt-template.yaml").getFile(); - return unifiedClient.prompt().importPromptTemplate("default", null, template); + return promptTemplatesClient.importPromptTemplate("default", null, template); } @GetMapping("/exportTemplate") byte[] exportTemplate() throws IOException { final var template = importTemplate(); - return unifiedClient.prompt().exportPromptTemplate(template.getId()); + return promptTemplatesClient.exportPromptTemplate(template.getId()); } @GetMapping("/useTemplate") PromptTemplateSubstitutionResponse useTemplate() { final var template = createTemplate(); - return unifiedClient - .prompt() - .parsePromptTemplateById( - template.getId(), - "default", - null, - false, - PromptTemplateSubstitutionRequest.create() - .inputParams(Map.of("inputExample", "I love football"))); + return promptTemplatesClient.parsePromptTemplateById( + template.getId(), + "default", + null, + false, + PromptTemplateSubstitutionRequest.create() + .inputParams(Map.of("inputExample", "I love football"))); } @GetMapping("/deleteTemplate") List deleteTemplate() { - final PromptTemplateListResponse templates = unifiedClient.prompt().listPromptTemplates(); + final PromptTemplateListResponse templates = promptTemplatesClient.listPromptTemplates(); return templates.getResources().stream() .filter(template -> NAME.equals(template.getName())) - .map(template -> unifiedClient.prompt().deletePromptTemplate(template.getId())) + .map(template -> promptTemplatesClient.deletePromptTemplate(template.getId())) .toList(); } @@ -161,7 +163,7 @@ Generation promptRegistryToSpringAi() { @GetMapping("/listOrchConfigs") OrchestrationConfigListResponse listOrchConfigs() { - return unifiedClient.orchestration().listOrchestrationConfigs(); + return orchestrationConfigsClient.listOrchestrationConfigs(); } @GetMapping("/createOrchConfig") @@ -187,17 +189,17 @@ OrchestrationConfigPostResponse createOrchConfig() { .version("0.0.1") .scenario("sdk-test-scenario") .spec(orchestrationConfig); - return unifiedClient.orchestration().createUpdateOrchestrationConfig(postRequest); + return orchestrationConfigsClient.createUpdateOrchestrationConfig(postRequest); } @GetMapping("/deleteOrchConfig") List deleteOrchConfig() { final OrchestrationConfigListResponse configs = - unifiedClient.orchestration().listOrchestrationConfigs(); + orchestrationConfigsClient.listOrchestrationConfigs(); return configs.getResources().stream() .filter(config -> NAME.equals(config.getName())) - .map(config -> unifiedClient.orchestration().deleteOrchestrationConfig(config.getId())) + .map(config -> orchestrationConfigsClient.deleteOrchestrationConfig(config.getId())) .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 afa6954bf..d3c76d1f4 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 @@ -896,13 +896,13 @@ private void ensureOrchestrationConfigExists(final String scenario, final String .version("0.0.1") .scenario(scenario) .spec(buildOrchestrationConfig()); - unifiedClient.orchestration().createUpdateOrchestrationConfig(postRequest); + unifiedClient.orchestrationConfig().createUpdateOrchestrationConfig(postRequest); } } private boolean orchConfigExists( final String configName, final PromptRegistryClient unifiedClient) { - return unifiedClient.orchestration().listOrchestrationConfigs().getResources().stream() + return unifiedClient.orchestrationConfig().listOrchestrationConfigs().getResources().stream() .anyMatch(resp -> resp.getName().equals(configName)); } @@ -977,7 +977,7 @@ public Stream streamDeltasWithReferenceById() ensureOrchestrationConfigExists("sdk-test-paraphrase", "create-3-paraphrases-of-sentence"); val unifiedClient = new PromptRegistryClient(); val id = - unifiedClient.orchestration().listOrchestrationConfigs().getResources().stream() + unifiedClient.orchestrationConfig().listOrchestrationConfigs().getResources().stream() .filter(r -> r.getName().equals("test-config-for-OrchestrationTest")) .findFirst() .orElseThrow() From 82f382e8ea1039a67d186f1a261211407e81306b Mon Sep 17 00:00:00 2001 From: Vladimir Aseev Date: Wed, 16 Sep 2026 14:28:37 +0200 Subject: [PATCH 05/10] polish code style --- .../OrchestrationConfigClientTest.java | 8 +- .../registry/PromptRegistryClientTest.java | 22 ++--- .../spring/SpringAiConverterTest.java | 97 +++++++++---------- docs/release_notes.md | 12 +-- .../app/services/OrchestrationService.java | 15 +-- 5 files changed, 75 insertions(+), 79 deletions(-) diff --git a/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/OrchestrationConfigClientTest.java b/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/OrchestrationConfigClientTest.java index ccbbb5c2d..f3bd2abbb 100644 --- a/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/OrchestrationConfigClientTest.java +++ b/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/OrchestrationConfigClientTest.java @@ -18,19 +18,19 @@ public class OrchestrationConfigClientTest { private static final WireMockExtension WM = WireMockExtension.newInstance().options(wireMockConfig().dynamicPort()).build(); - private static OrchestrationConfigsApi orchestrationConfigsClient; + private static OrchestrationConfigsApi client; @BeforeAll static void setup() { final HttpDestination destination = DefaultHttpDestination.builder(WM.baseUrl()).build(); final AiCoreService service = new AiCoreService().withBaseDestination(destination); - final var client = new PromptRegistryClient(service); - orchestrationConfigsClient = client.orchestrationConfig(); + final var promptRegistryClient = new PromptRegistryClient(service); + client = promptRegistryClient.orchestrationConfig(); } @Test void testPipelines() { - final var result = orchestrationConfigsClient.listOrchestrationConfigs(); + final var result = client.listOrchestrationConfigs(); assertThat(result.getCount()).isEqualTo(2); assertThat(result.getResources()).hasSize(2); final var template = result.getResources().get(0); diff --git a/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/PromptRegistryClientTest.java b/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/PromptRegistryClientTest.java index d33ecdd4b..715bb0054 100644 --- a/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/PromptRegistryClientTest.java +++ b/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/PromptRegistryClientTest.java @@ -35,7 +35,7 @@ class PromptRegistryClientTest { private static final WireMockExtension WM = WireMockExtension.newInstance().options(wireMockConfig().dynamicPort()).build(); - private static PromptTemplatesApi promptClient; + private static PromptTemplatesApi client; private static ServiceBindingAccessor originalAccessor; @BeforeAll @@ -43,8 +43,8 @@ static void setup() { originalAccessor = DefaultServiceBindingAccessor.getInstance(); final HttpDestination destination = DefaultHttpDestination.builder(WM.baseUrl()).build(); final AiCoreService service = new AiCoreService().withBaseDestination(destination); - PromptRegistryClient client = new PromptRegistryClient(service); - promptClient = client.prompt(); + PromptRegistryClient promptRegistryClient = new PromptRegistryClient(service); + client = promptRegistryClient.prompt(); } @AfterEach @@ -72,7 +72,7 @@ void testDefaultConstructor() { @Test void testPipelines() { - final var result = promptClient.listPromptTemplates(); + final var result = client.listPromptTemplates(); assertThat(result.getCount()).isEqualTo(2); assertThat(result.getResources()).hasSize(2); final var template = result.getResources().get(0); @@ -89,7 +89,7 @@ void testPipelines() { @Test void testGetTemplateWithResponseFormatText() { final var uuid = UUID.fromString("22117a64-9f2c-481b-9402-8acb66eeb707"); - final PromptTemplateGetResponse response = promptClient.getPromptTemplateByUuid(uuid); + final PromptTemplateGetResponse response = client.getPromptTemplateByUuid(uuid); assertThat(response.getName()).isEqualTo("test"); assertThat(response.getVersion()).isEqualTo("0.0.1"); @@ -104,7 +104,7 @@ void testGetTemplateWithResponseFormatText() { @Test void testGetTemplateWithResponseFormatJsonObject() { final var uuid = UUID.fromString("21cb1358-0bf1-4f43-870b-00f14d0f9f16"); - final var response = promptClient.getPromptTemplateByUuid(uuid); + final var response = client.getPromptTemplateByUuid(uuid); assertThat(response.getName()).isEqualTo("test"); assertThat(response.getVersion()).isEqualTo("0.0.1"); @@ -119,7 +119,7 @@ void testGetTemplateWithResponseFormatJsonObject() { @Test void testGetTemplateWithResponseFormatJsonSchema() { final var uuid = UUID.fromString("0f79fec4-ae07-4c35-96e3-df7f4a3f1df5"); - final var response = promptClient.getPromptTemplateByUuid(uuid); + final var response = client.getPromptTemplateByUuid(uuid); assertThat(response.getName()).isEqualTo("test"); assertThat(response.getVersion()).isEqualTo("0.0.1"); @@ -139,7 +139,7 @@ void testGetTemplateWithResponseFormatJsonSchema() { @Test void testGetTemplateWithMultiChatTemplate() { final var uuid = UUID.fromString("8f79fec4-ae07-4c35-96e3-df7f4a3f1df5"); - final var response = promptClient.getPromptTemplateByUuid(uuid); + final var response = client.getPromptTemplateByUuid(uuid); assertThat(response.getSpec()).isNotNull(); assertThat(response.getSpec().getTemplate()).hasSize(2); @@ -160,7 +160,7 @@ void testGetTemplateWithMultiChatTemplate() { void testGetTemplateWithInvalidRoleType() { final var uuid = UUID.fromString("45cb1358-0bf1-4f43-870b-00f14d0f9f16"); - assertThatThrownBy(() -> promptClient.getPromptTemplateByUuid(uuid)) + assertThatThrownBy(() -> client.getPromptTemplateByUuid(uuid)) .hasStackTraceContaining("PromptTemplate requires textual 'role' property."); } @@ -168,7 +168,7 @@ void testGetTemplateWithInvalidRoleType() { void testGetTemplateWithInvalidContentType() { final var uuid = UUID.fromString("55cb1358-0bf1-4f43-870b-00f14d0f9f16"); - assertThatThrownBy(() -> promptClient.getPromptTemplateByUuid(uuid)) + assertThatThrownBy(() -> client.getPromptTemplateByUuid(uuid)) .hasStackTraceContaining( "PromptTemplate content must be either a string or an array, but found: BOOLEAN"); } @@ -180,7 +180,7 @@ void testParsePromptTemplateHotPath() { .inputParams(Map.of("inputExample", "I love football")); final var response = - promptClient.parsePromptTemplateByNameVersion( + client.parsePromptTemplateByNameVersion( "categorization", "0.0.1", "hotpath-serde", "default", null, false, request); assertThat(response.getParsedPrompt()).hasSize(2); 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 a5f642731..bbf2d63ee 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 @@ -7,16 +7,17 @@ import com.github.tomakehurst.wiremock.junit5.WireMockExtension; import com.sap.ai.sdk.core.AiCoreService; import com.sap.ai.sdk.prompt.registry.PromptRegistryClient; +import com.sap.ai.sdk.prompt.registry.client.PromptTemplatesApi; import com.sap.ai.sdk.prompt.registry.model.MultiChatContent; import com.sap.ai.sdk.prompt.registry.model.MultiChatTemplate; import com.sap.ai.sdk.prompt.registry.model.PromptTemplate; import com.sap.ai.sdk.prompt.registry.model.PromptTemplateSubstitutionRequest; import com.sap.ai.sdk.prompt.registry.model.PromptTemplateSubstitutionResponse; 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.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.springframework.ai.chat.messages.Message; @@ -28,24 +29,27 @@ public class SpringAiConverterTest { 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); + private static PromptTemplatesApi client; + + @BeforeAll + static void setup() { + val destination = DefaultHttpDestination.builder(WM.baseUrl()).build(); + val service = new AiCoreService().withBaseDestination(destination); + client = new PromptRegistryClient(service).prompt(); + } @Test void testPromptRegistryToSpringAi() { - var client = new PromptRegistryClient(SERVICE); val promptResponse = - client - .prompt() - .parsePromptTemplateByNameVersion( - "categorization", - "0.0.1", - "java-e2e-test", - "default", - null, - false, - PromptTemplateSubstitutionRequest.create() - .inputParams(Map.of("inputExample", "I love football"))); + client.parsePromptTemplateByNameVersion( + "categorization", + "0.0.1", + "java-e2e-test", + "default", + null, + false, + PromptTemplateSubstitutionRequest.create() + .inputParams(Map.of("inputExample", "I love football"))); List messages = SpringAiConverter.promptTemplateToMessages(promptResponse); assertThat(messages) @@ -58,19 +62,16 @@ void testPromptRegistryToSpringAi() { @Test void testInvalidRoleThrowsException() { - var client = new PromptRegistryClient(SERVICE); val errorPrompt = - client - .prompt() - .parsePromptTemplateByNameVersion( - "categorization", - "0.0.1", - "error", - "default", - null, - false, - PromptTemplateSubstitutionRequest.create() - .inputParams(Map.of("inputExample", "I love football"))); + client.parsePromptTemplateByNameVersion( + "categorization", + "0.0.1", + "error", + "default", + null, + false, + PromptTemplateSubstitutionRequest.create() + .inputParams(Map.of("inputExample", "I love football"))); assertThatThrownBy(() -> SpringAiConverter.promptTemplateToMessages(errorPrompt)) .isInstanceOf(IllegalArgumentException.class) @@ -79,19 +80,16 @@ void testInvalidRoleThrowsException() { @Test void testMultiChatTemplateTextContentToSpringAi() { - var client = new PromptRegistryClient(SERVICE); val promptResponse = - client - .prompt() - .parsePromptTemplateByNameVersion( - "categorization", - "0.0.1", - "multi-text", - "default", - null, - false, - PromptTemplateSubstitutionRequest.create() - .inputParams(Map.of("inputExample", "I love football"))); + client.parsePromptTemplateByNameVersion( + "categorization", + "0.0.1", + "multi-text", + "default", + null, + false, + PromptTemplateSubstitutionRequest.create() + .inputParams(Map.of("inputExample", "I love football"))); List messages = SpringAiConverter.promptTemplateToMessages(promptResponse); assertThat(messages) @@ -100,19 +98,16 @@ void testMultiChatTemplateTextContentToSpringAi() { @Test void testMultiChatTemplateImageContentThrowsException() { - var client = new PromptRegistryClient(SERVICE); val promptResponse = - client - .prompt() - .parsePromptTemplateByNameVersion( - "categorization", - "0.0.1", - "multi-image", - "default", - null, - false, - PromptTemplateSubstitutionRequest.create() - .inputParams(Map.of("inputExample", "I love football"))); + client.parsePromptTemplateByNameVersion( + "categorization", + "0.0.1", + "multi-image", + "default", + null, + false, + PromptTemplateSubstitutionRequest.create() + .inputParams(Map.of("inputExample", "I love football"))); assertThatThrownBy(() -> SpringAiConverter.promptTemplateToMessages(promptResponse)) .isInstanceOf(UnsupportedOperationException.class) diff --git a/docs/release_notes.md b/docs/release_notes.md index d3833a6ef..d4505038e 100644 --- a/docs/release_notes.md +++ b/docs/release_notes.md @@ -19,11 +19,11 @@ OrchestrationConfigClient and PromptClient were replaced with unified PromptRegi OrchestrationConfigClient: ```diff --var orchestrationConfigClient = new OrchestrationConfigClient(); --var configs = orchestrationConfigClient.listOrchestrationConfigs(); +-var orchestrationConfigsClient = new OrchestrationConfigClient(); +-var configs = orchestrationConfigsClient.listOrchestrationConfigs(); -+var promptRegistryClient = new PromptRegistryClient(); -+var configs = promptRegistryClient.orchestrationConfig().listOrchestrationConfigs(); ++var orchestractionConfigsClient = new PromptRegistryClient().orchestrationConfig(); ++var configs = orchestractionConfigsClient.listOrchestrationConfigs(); ``` PromptRegistryClient: @@ -31,8 +31,8 @@ PromptRegistryClient: -var promptClient = new PromptClient(); -var templates = promptClient.listPromptTemplates(); -+var promptRegistryClient = new PromptRegistryClient(); -+var templates = promptRegistryClient.prompt().listPromptTemplates(); ++var promptClient = new PromptRegistryClient().prompt(); ++var templates = promptClient.listPromptTemplates(); ``` #### Spring AI 2.0.1 Migration Guide 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 d3c76d1f4..bfd224c29 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 @@ -47,6 +47,7 @@ import com.sap.ai.sdk.orchestration.model.SearchSelectOptionEnum; import com.sap.ai.sdk.orchestration.model.Template; import com.sap.ai.sdk.prompt.registry.PromptRegistryClient; +import com.sap.ai.sdk.prompt.registry.client.OrchestrationConfigsApi; import com.sap.ai.sdk.prompt.registry.model.LLMModelDetails; import com.sap.ai.sdk.prompt.registry.model.OrchestrationConfigPostRequest; import com.sap.ai.sdk.prompt.registry.model.PartialModuleConfigs; @@ -888,21 +889,21 @@ public OrchestrationChatResponse executeConfigFromReference() { } private void ensureOrchestrationConfigExists(final String scenario, final String name) { - final PromptRegistryClient unifiedClient = new PromptRegistryClient(); - if (!orchConfigExists("test-config-for-OrchestrationTest", unifiedClient)) { + final OrchestrationConfigsApi client = new PromptRegistryClient().orchestrationConfig(); + if (!orchConfigExists("test-config-for-OrchestrationTest", client)) { final OrchestrationConfigPostRequest postRequest = OrchestrationConfigPostRequest.create() .name(name) .version("0.0.1") .scenario(scenario) .spec(buildOrchestrationConfig()); - unifiedClient.orchestrationConfig().createUpdateOrchestrationConfig(postRequest); + client.createUpdateOrchestrationConfig(postRequest); } } private boolean orchConfigExists( - final String configName, final PromptRegistryClient unifiedClient) { - return unifiedClient.orchestrationConfig().listOrchestrationConfigs().getResources().stream() + final String configName, final OrchestrationConfigsApi orchestrationConfigsClient) { + return orchestrationConfigsClient.listOrchestrationConfigs().getResources().stream() .anyMatch(resp -> resp.getName().equals(configName)); } @@ -975,9 +976,9 @@ public Stream streamDeltasWithInlineConfig( public Stream streamDeltasWithReferenceById() { // get a valid id ensureOrchestrationConfigExists("sdk-test-paraphrase", "create-3-paraphrases-of-sentence"); - val unifiedClient = new PromptRegistryClient(); + val orchestrationConfigsClient = new PromptRegistryClient().orchestrationConfig(); val id = - unifiedClient.orchestrationConfig().listOrchestrationConfigs().getResources().stream() + orchestrationConfigsClient.listOrchestrationConfigs().getResources().stream() .filter(r -> r.getName().equals("test-config-for-OrchestrationTest")) .findFirst() .orElseThrow() From d2104a80c017ef60eddb611e3e5978bb6bdef635 Mon Sep 17 00:00:00 2001 From: Vladimir Aseev Date: Wed, 16 Sep 2026 15:01:29 +0200 Subject: [PATCH 06/10] improve code style --- core-services/prompt-registry/pom.xml | 14 ++------ .../registry/PromptRegistryClientTest.java | 32 ------------------- 2 files changed, 2 insertions(+), 44 deletions(-) diff --git a/core-services/prompt-registry/pom.xml b/core-services/prompt-registry/pom.xml index b07ec1b5d..2738f5849 100644 --- a/core-services/prompt-registry/pom.xml +++ b/core-services/prompt-registry/pom.xml @@ -38,11 +38,11 @@ ${project.basedir}/../../ - 91% + 88% 93% 94% 100% - 83% + 80% 100% @@ -103,16 +103,6 @@ assertj-core test - - com.sap.cloud.environment.servicebinding.api - java-access-api - test - - - com.sap.cloud.environment.servicebinding.api - java-core-api - test - diff --git a/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/PromptRegistryClientTest.java b/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/PromptRegistryClientTest.java index 715bb0054..16eea6301 100644 --- a/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/PromptRegistryClientTest.java +++ b/core-services/prompt-registry/src/test/java/com/sap/ai/sdk/prompt/registry/PromptRegistryClientTest.java @@ -2,7 +2,6 @@ import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatCode; import static org.assertj.core.api.Assertions.assertThatThrownBy; import com.github.tomakehurst.wiremock.junit5.WireMockExtension; @@ -16,16 +15,10 @@ import com.sap.ai.sdk.prompt.registry.model.ResponseFormatText; import com.sap.ai.sdk.prompt.registry.model.SingleChatTemplate; import com.sap.ai.sdk.prompt.registry.model.TextContent; -import com.sap.cloud.environment.servicebinding.api.DefaultServiceBindingAccessor; -import com.sap.cloud.environment.servicebinding.api.DefaultServiceBindingBuilder; -import com.sap.cloud.environment.servicebinding.api.ServiceBindingAccessor; -import com.sap.cloud.environment.servicebinding.api.ServiceIdentifier; 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 java.util.UUID; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -36,40 +29,15 @@ class PromptRegistryClientTest { WireMockExtension.newInstance().options(wireMockConfig().dynamicPort()).build(); private static PromptTemplatesApi client; - private static ServiceBindingAccessor originalAccessor; @BeforeAll static void setup() { - originalAccessor = DefaultServiceBindingAccessor.getInstance(); final HttpDestination destination = DefaultHttpDestination.builder(WM.baseUrl()).build(); final AiCoreService service = new AiCoreService().withBaseDestination(destination); PromptRegistryClient promptRegistryClient = new PromptRegistryClient(service); client = promptRegistryClient.prompt(); } - @AfterEach - void teardown() { - DefaultServiceBindingAccessor.setInstance(originalAccessor); - } - - @Test - void testDefaultConstructor() { - final var binding = - new DefaultServiceBindingBuilder() - .withServiceIdentifier(ServiceIdentifier.AI_CORE) - .withCredentials( - Map.of( - "clientid", "client-id", - "clientsecret", "client-secret", - "credential-type", "binding-secret", - "url", WM.baseUrl(), - "serviceurls", Map.of("AI_API_URL", WM.baseUrl()))) - .build(); - DefaultServiceBindingAccessor.setInstance(() -> List.of(binding)); - - assertThatCode(PromptRegistryClient::new).doesNotThrowAnyException(); - } - @Test void testPipelines() { final var result = client.listPromptTemplates(); From 13d8aa08cb6f25ad7eb49fe2e9270faa029c6200 Mon Sep 17 00:00:00 2001 From: Vladimir Aseev Date: Wed, 16 Sep 2026 15:14:23 +0200 Subject: [PATCH 07/10] improve code style --- .../controllers/PromptRegistryController.java | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) 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 3638c9022..d962c39f3 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 @@ -50,26 +50,26 @@ @RequestMapping("/prompt-registry") class PromptRegistryController { static final String NAME = "java-e2e-test"; - private static final OrchestrationConfigsApi orchestrationConfigsClient = + private static final OrchestrationConfigsApi orchConfigClient = new PromptRegistryClient().orchestrationConfig(); - private static final PromptTemplatesApi promptTemplatesClient = + private static final PromptTemplatesApi promptClient = new PromptRegistryClient().prompt(); @GetMapping("/listTemplates") PromptTemplateListResponse listTemplates() { - return promptTemplatesClient.listPromptTemplates(); + return promptClient.listPromptTemplates(); } @GetMapping("/createTemplate") PromptTemplatePostResponse createTemplate() { - return promptTemplatesClient.createUpdatePromptTemplate(getTemplate("Finance, Tech, Sports")); + return promptClient.createUpdatePromptTemplate(getTemplate("Finance, Tech, Sports")); } @GetMapping("/updateTemplate") PromptTemplatePostResponse updateTemplate() { // create template then update - promptTemplatesClient.createUpdatePromptTemplate(getTemplate("Finance, Tech, Sports")); - return promptTemplatesClient.createUpdatePromptTemplate( + promptClient.createUpdatePromptTemplate(getTemplate("Finance, Tech, Sports")); + return promptClient.createUpdatePromptTemplate( getTemplate("Finance, Tech, Sports, Politics")); } @@ -93,25 +93,25 @@ private PromptTemplatePostRequest getTemplate(final String categories) { @GetMapping("/history") PromptTemplateListResponse history() { - return promptTemplatesClient.listPromptTemplateHistory("categorization", "0.0.1", NAME); + return promptClient.listPromptTemplateHistory("categorization", "0.0.1", NAME); } @GetMapping("/importTemplate") PromptTemplatePostResponse importTemplate() throws IOException { val template = new ClassPathResource("prompt-template.yaml").getFile(); - return promptTemplatesClient.importPromptTemplate("default", null, template); + return promptClient.importPromptTemplate("default", null, template); } @GetMapping("/exportTemplate") byte[] exportTemplate() throws IOException { final var template = importTemplate(); - return promptTemplatesClient.exportPromptTemplate(template.getId()); + return promptClient.exportPromptTemplate(template.getId()); } @GetMapping("/useTemplate") PromptTemplateSubstitutionResponse useTemplate() { final var template = createTemplate(); - return promptTemplatesClient.parsePromptTemplateById( + return promptClient.parsePromptTemplateById( template.getId(), "default", null, @@ -122,11 +122,11 @@ PromptTemplateSubstitutionResponse useTemplate() { @GetMapping("/deleteTemplate") List deleteTemplate() { - final PromptTemplateListResponse templates = promptTemplatesClient.listPromptTemplates(); + final PromptTemplateListResponse templates = promptClient.listPromptTemplates(); return templates.getResources().stream() .filter(template -> NAME.equals(template.getName())) - .map(template -> promptTemplatesClient.deletePromptTemplate(template.getId())) + .map(template -> promptClient.deletePromptTemplate(template.getId())) .toList(); } @@ -163,7 +163,7 @@ Generation promptRegistryToSpringAi() { @GetMapping("/listOrchConfigs") OrchestrationConfigListResponse listOrchConfigs() { - return orchestrationConfigsClient.listOrchestrationConfigs(); + return orchConfigClient.listOrchestrationConfigs(); } @GetMapping("/createOrchConfig") @@ -189,17 +189,17 @@ OrchestrationConfigPostResponse createOrchConfig() { .version("0.0.1") .scenario("sdk-test-scenario") .spec(orchestrationConfig); - return orchestrationConfigsClient.createUpdateOrchestrationConfig(postRequest); + return orchConfigClient.createUpdateOrchestrationConfig(postRequest); } @GetMapping("/deleteOrchConfig") List deleteOrchConfig() { final OrchestrationConfigListResponse configs = - orchestrationConfigsClient.listOrchestrationConfigs(); + orchConfigClient.listOrchestrationConfigs(); return configs.getResources().stream() .filter(config -> NAME.equals(config.getName())) - .map(config -> orchestrationConfigsClient.deleteOrchestrationConfig(config.getId())) + .map(config -> orchConfigClient.deleteOrchestrationConfig(config.getId())) .toList(); } } From 945c411f5cb449f595b9c583cb9bf8c1dc6e6b75 Mon Sep 17 00:00:00 2001 From: SAP Cloud SDK Bot Date: Wed, 16 Sep 2026 13:15:07 +0000 Subject: [PATCH 08/10] Formatting --- .../ai/sdk/app/controllers/PromptRegistryController.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) 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 d962c39f3..dd57b4920 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 @@ -52,8 +52,7 @@ class PromptRegistryController { static final String NAME = "java-e2e-test"; private static final OrchestrationConfigsApi orchConfigClient = new PromptRegistryClient().orchestrationConfig(); - private static final PromptTemplatesApi promptClient = - new PromptRegistryClient().prompt(); + private static final PromptTemplatesApi promptClient = new PromptRegistryClient().prompt(); @GetMapping("/listTemplates") PromptTemplateListResponse listTemplates() { @@ -69,8 +68,7 @@ PromptTemplatePostResponse createTemplate() { PromptTemplatePostResponse updateTemplate() { // create template then update promptClient.createUpdatePromptTemplate(getTemplate("Finance, Tech, Sports")); - return promptClient.createUpdatePromptTemplate( - getTemplate("Finance, Tech, Sports, Politics")); + return promptClient.createUpdatePromptTemplate(getTemplate("Finance, Tech, Sports, Politics")); } private PromptTemplatePostRequest getTemplate(final String categories) { @@ -194,8 +192,7 @@ OrchestrationConfigPostResponse createOrchConfig() { @GetMapping("/deleteOrchConfig") List deleteOrchConfig() { - final OrchestrationConfigListResponse configs = - orchConfigClient.listOrchestrationConfigs(); + final OrchestrationConfigListResponse configs = orchConfigClient.listOrchestrationConfigs(); return configs.getResources().stream() .filter(config -> NAME.equals(config.getName())) From 1266c400f515f98c2849e823308e948c446bd9a6 Mon Sep 17 00:00:00 2001 From: Vladimir Aseev Date: Wed, 16 Sep 2026 15:20:03 +0200 Subject: [PATCH 09/10] improve code style --- .../sdk/app/controllers/PromptRegistryController.java | 5 +---- .../sap/ai/sdk/app/services/OrchestrationService.java | 10 +++++----- 2 files changed, 6 insertions(+), 9 deletions(-) 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 d962c39f3..bab39b134 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 @@ -138,10 +138,7 @@ Generation promptRegistryToSpringAi() { val advisor = MessageChatMemoryAdvisor.builder(memory).build(); val cl = ChatClient.builder(openAiClient).defaultAdvisors(advisor).build(); - val promptResponse = - new PromptRegistryClient() - .prompt() - .parsePromptTemplateByNameVersion( + val promptResponse = promptClient.parsePromptTemplateByNameVersion( "categorization", "0.0.1", "java-e2e-test", 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 bfd224c29..df38beb01 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 @@ -889,15 +889,15 @@ public OrchestrationChatResponse executeConfigFromReference() { } private void ensureOrchestrationConfigExists(final String scenario, final String name) { - final OrchestrationConfigsApi client = new PromptRegistryClient().orchestrationConfig(); - if (!orchConfigExists("test-config-for-OrchestrationTest", client)) { + final OrchestrationConfigsApi orchConfigClient = new PromptRegistryClient().orchestrationConfig(); + if (!orchConfigExists("test-config-for-OrchestrationTest", orchConfigClient)) { final OrchestrationConfigPostRequest postRequest = OrchestrationConfigPostRequest.create() .name(name) .version("0.0.1") .scenario(scenario) .spec(buildOrchestrationConfig()); - client.createUpdateOrchestrationConfig(postRequest); + orchConfigClient.createUpdateOrchestrationConfig(postRequest); } } @@ -976,9 +976,9 @@ public Stream streamDeltasWithInlineConfig( public Stream streamDeltasWithReferenceById() { // get a valid id ensureOrchestrationConfigExists("sdk-test-paraphrase", "create-3-paraphrases-of-sentence"); - val orchestrationConfigsClient = new PromptRegistryClient().orchestrationConfig(); + val orchConfigClient = new PromptRegistryClient().orchestrationConfig(); val id = - orchestrationConfigsClient.listOrchestrationConfigs().getResources().stream() + orchConfigClient.listOrchestrationConfigs().getResources().stream() .filter(r -> r.getName().equals("test-config-for-OrchestrationTest")) .findFirst() .orElseThrow() From 2fcc3a37639dcd41bddea244c10da99849d5e536 Mon Sep 17 00:00:00 2001 From: SAP Cloud SDK Bot Date: Wed, 16 Sep 2026 13:21:08 +0000 Subject: [PATCH 10/10] Formatting --- .../controllers/PromptRegistryController.java | 19 ++++++++++--------- .../app/services/OrchestrationService.java | 5 +++-- 2 files changed, 13 insertions(+), 11 deletions(-) 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 6d45a00c3..a130d425d 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 @@ -136,15 +136,16 @@ Generation promptRegistryToSpringAi() { val advisor = MessageChatMemoryAdvisor.builder(memory).build(); val cl = ChatClient.builder(openAiClient).defaultAdvisors(advisor).build(); - val promptResponse = promptClient.parsePromptTemplateByNameVersion( - "categorization", - "0.0.1", - "java-e2e-test", - "default", - null, - false, - PromptTemplateSubstitutionRequest.create() - .inputParams(Map.of("inputExample", "I love football"))); + val promptResponse = + promptClient.parsePromptTemplateByNameVersion( + "categorization", + "0.0.1", + "java-e2e-test", + "default", + null, + false, + PromptTemplateSubstitutionRequest.create() + .inputParams(Map.of("inputExample", "I love football"))); final List messages = SpringAiConverter.promptTemplateToMessages(promptResponse); val prompt = new Prompt(messages); 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 df38beb01..3f7fbc952 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 @@ -889,7 +889,8 @@ public OrchestrationChatResponse executeConfigFromReference() { } private void ensureOrchestrationConfigExists(final String scenario, final String name) { - final OrchestrationConfigsApi orchConfigClient = new PromptRegistryClient().orchestrationConfig(); + final OrchestrationConfigsApi orchConfigClient = + new PromptRegistryClient().orchestrationConfig(); if (!orchConfigExists("test-config-for-OrchestrationTest", orchConfigClient)) { final OrchestrationConfigPostRequest postRequest = OrchestrationConfigPostRequest.create() @@ -978,7 +979,7 @@ public Stream streamDeltasWithReferenceById() ensureOrchestrationConfigExists("sdk-test-paraphrase", "create-3-paraphrases-of-sentence"); val orchConfigClient = new PromptRegistryClient().orchestrationConfig(); val id = - orchConfigClient.listOrchestrationConfigs().getResources().stream() + orchConfigClient.listOrchestrationConfigs().getResources().stream() .filter(r -> r.getName().equals("test-config-for-OrchestrationTest")) .findFirst() .orElseThrow()