diff --git a/core-services/prompt-registry/pom.xml b/core-services/prompt-registry/pom.xml index 1493c0c49..73f879e58 100644 --- a/core-services/prompt-registry/pom.xml +++ b/core-services/prompt-registry/pom.xml @@ -38,10 +38,10 @@ ${project.basedir}/../../ - 84% + 73% 90% 92% - 100% + 78% 80% 100% diff --git a/docs/release_notes.md b/docs/release_notes.md index 5745d6681..7ad885c4e 100644 --- a/docs/release_notes.md +++ b/docs/release_notes.md @@ -16,7 +16,7 @@ ### 📈 Improvements -- +-[Orchestration] Added new API OrchestrationTemplateReference#withScope to support RG-scoped prompt templates. ### 🐛 Fixed Issues diff --git a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationClientException.java b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationClientException.java index 3df999b9b..929ea27a5 100644 --- a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationClientException.java +++ b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationClientException.java @@ -29,6 +29,11 @@ public class OrchestrationClientException extends ClientException { (message, clientError, cause) -> { final var details = extractInputFilterDetails(clientError); if (details.isEmpty()) { + if (message.equals( + "Request failed with status 404 (Not Found): 404 - Templating Module: No Prompt Template found in the Prompt Registry.")) { + message += + "\n Please verify that the provided resource group id is correct and matches the provided template reference details if the template is referenced from a resource-group scope, otherwise use the tenant scope without resource group id header."; + } return new OrchestrationClientException(message, cause).setClientError(clientError); } return new Input(message, cause).setFilterDetails(details).setClientError(clientError); diff --git a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationTemplateReference.java b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationTemplateReference.java index e88ac06a3..1acd89eee 100644 --- a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationTemplateReference.java +++ b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationTemplateReference.java @@ -3,12 +3,15 @@ import com.google.common.annotations.Beta; import com.sap.ai.sdk.orchestration.model.PromptTemplatingModuleConfigPrompt; import com.sap.ai.sdk.orchestration.model.TemplateRef; +import com.sap.ai.sdk.orchestration.model.TemplateRefByID; +import com.sap.ai.sdk.orchestration.model.TemplateRefByScenarioNameVersion; import com.sap.ai.sdk.orchestration.model.TemplateRefTemplateRef; import javax.annotation.Nonnull; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; import lombok.Value; +import lombok.With; /** * A reference to a template to use in {@link OrchestrationModuleConfig}. @@ -22,6 +25,8 @@ public class OrchestrationTemplateReference extends TemplateConfig { @Nonnull TemplateRefTemplateRef reference; + @With @Nonnull ScopeEnum scope; + /** * Create a low-level representation of the template. * @@ -30,6 +35,25 @@ public class OrchestrationTemplateReference extends TemplateConfig { @Nonnull @Override protected PromptTemplatingModuleConfigPrompt toLowLevel() { - return TemplateRef.create().templateRef(reference); + if (reference instanceof TemplateRefByID idRef) { + final var valueById = TemplateRefByID.ScopeEnum.valueOf(scope.name()); + idRef.setScope(valueById); + return TemplateRef.create().templateRef(idRef); + } else if (reference instanceof TemplateRefByScenarioNameVersion scenarioRef) { + final var valueByScenario = TemplateRefByScenarioNameVersion.ScopeEnum.valueOf(scope.name()); + scenarioRef.setScope(valueByScenario); + return TemplateRef.create().templateRef(scenarioRef); + } else { + throw new IllegalStateException( + "Unsupported template reference type: " + reference.getClass()); + } + } + + /** The scope of the template reference. */ + public enum ScopeEnum { + /** Template is resolved within the current tenant scope. */ + TENANT, + /** Template is resolved within the configured resource group scope. */ + RESOURCE_GROUP } } diff --git a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/TemplateConfig.java b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/TemplateConfig.java index 15bab9c85..f0a328e97 100644 --- a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/TemplateConfig.java +++ b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/TemplateConfig.java @@ -1,5 +1,7 @@ package com.sap.ai.sdk.orchestration; +import static com.sap.ai.sdk.orchestration.OrchestrationTemplateReference.ScopeEnum.TENANT; + import com.google.common.annotations.Beta; import com.sap.ai.sdk.orchestration.model.PromptTemplatingModuleConfigPrompt; import com.sap.ai.sdk.orchestration.model.TemplateRefByID; @@ -35,28 +37,33 @@ public static OrchestrationTemplate create() { } /** - * Build a template reference. + * Build a template reference with tenant level scope. * * @return An intermediate object to build the template reference. */ @Nonnull public static ReferenceBuilder reference() { final var templ = TemplateRefByScenarioNameVersion.create(); - return s -> n -> v -> new OrchestrationTemplateReference(templ.scenario(s).name(n).version(v)); + + return scenario -> + name -> + version -> + new OrchestrationTemplateReference( + templ.scenario(scenario).name(name).version(version), TENANT); } /** Intermediate object to build a template reference. */ @FunctionalInterface public interface ReferenceBuilder { /** - * Build a template reference with the given id. + * Build a template reference with the given id for tenant scope. * * @param id The id of the template. * @return A template reference with the given id. */ @Nonnull default OrchestrationTemplateReference byId(@Nonnull final String id) { - return new OrchestrationTemplateReference(TemplateRefByID.create().id(id)); + return new OrchestrationTemplateReference(TemplateRefByID.create().id(id), TENANT); } /** diff --git a/orchestration/src/test/java/com/sap/ai/sdk/orchestration/OrchestrationConvenienceUnitTest.java b/orchestration/src/test/java/com/sap/ai/sdk/orchestration/OrchestrationConvenienceUnitTest.java index 6ac9a7e83..da26e2adb 100644 --- a/orchestration/src/test/java/com/sap/ai/sdk/orchestration/OrchestrationConvenienceUnitTest.java +++ b/orchestration/src/test/java/com/sap/ai/sdk/orchestration/OrchestrationConvenienceUnitTest.java @@ -1,5 +1,7 @@ package com.sap.ai.sdk.orchestration; +import static com.sap.ai.sdk.orchestration.OrchestrationTemplateReference.ScopeEnum.RESOURCE_GROUP; +import static com.sap.ai.sdk.orchestration.OrchestrationTemplateReference.ScopeEnum.TENANT; import static com.sap.ai.sdk.orchestration.model.UserChatMessage.RoleEnum.USER; import static org.assertj.core.api.Assertions.assertThat; @@ -160,20 +162,35 @@ void testTemplateConstruction() { void testTemplateReferenceConstruction() { var templateReferenceId = TemplateConfig.reference().byId("id"); var expectedTemplateReferenceId = - new OrchestrationTemplateReference(TemplateRefByID.create().id("id")); + new OrchestrationTemplateReference(TemplateRefByID.create().id("id"), TENANT); var templateReferenceIdLowLevel = TemplateRef.create().templateRef(TemplateRefByID.create().id("id")); assertThat(templateReferenceId).isEqualTo(expectedTemplateReferenceId); assertThat(templateReferenceId.toLowLevel()).isEqualTo(templateReferenceIdLowLevel); + templateReferenceId = TemplateConfig.reference().byId("id").withScope(RESOURCE_GROUP); + expectedTemplateReferenceId = + new OrchestrationTemplateReference(TemplateRefByID.create().id("id"), RESOURCE_GROUP); + templateReferenceIdLowLevel = + TemplateRef.create() + .templateRef( + TemplateRefByID.create().id("id").scope(TemplateRefByID.ScopeEnum.RESOURCE_GROUP)); + assertThat(templateReferenceId).isEqualTo(expectedTemplateReferenceId); + assertThat(templateReferenceId.toLowLevel()).isEqualTo(templateReferenceIdLowLevel); + var templateReferenceScenarioNameVersion = - TemplateConfig.reference().byScenario("scenario").name("name").version("version"); + TemplateConfig.reference() + .byScenario("scenario") + .name("name") + .version("version") + .withScope(TENANT); var expectedTemplateReferenceScenarioNameVersion = new OrchestrationTemplateReference( TemplateRefByScenarioNameVersion.create() .scenario("scenario") .name("name") - .version("version")); + .version("version"), + TENANT); var templateReferenceScenarioNameVersionLowLevel = TemplateRef.create() .templateRef( @@ -185,6 +202,33 @@ void testTemplateReferenceConstruction() { .isEqualTo(expectedTemplateReferenceScenarioNameVersion); assertThat(templateReferenceScenarioNameVersion.toLowLevel()) .isEqualTo(templateReferenceScenarioNameVersionLowLevel); + + templateReferenceScenarioNameVersion = + TemplateConfig.reference() + .byScenario("scenario") + .name("name") + .version("version") + .withScope(RESOURCE_GROUP); + var scopeScenario = TemplateRefByScenarioNameVersion.ScopeEnum.RESOURCE_GROUP; + expectedTemplateReferenceScenarioNameVersion = + new OrchestrationTemplateReference( + TemplateRefByScenarioNameVersion.create() + .scenario("scenario") + .name("name") + .version("version"), + RESOURCE_GROUP); + templateReferenceScenarioNameVersionLowLevel = + TemplateRef.create() + .templateRef( + TemplateRefByScenarioNameVersion.create() + .scenario("scenario") + .name("name") + .version("version") + .scope(scopeScenario)); + assertThat(templateReferenceScenarioNameVersion) + .isEqualTo(expectedTemplateReferenceScenarioNameVersion); + assertThat(templateReferenceScenarioNameVersion.toLowLevel()) + .isEqualTo(templateReferenceScenarioNameVersionLowLevel); } @Test diff --git a/orchestration/src/test/java/com/sap/ai/sdk/orchestration/OrchestrationUnitTest.java b/orchestration/src/test/java/com/sap/ai/sdk/orchestration/OrchestrationUnitTest.java index c5b6540ef..2d6ba8811 100644 --- a/orchestration/src/test/java/com/sap/ai/sdk/orchestration/OrchestrationUnitTest.java +++ b/orchestration/src/test/java/com/sap/ai/sdk/orchestration/OrchestrationUnitTest.java @@ -17,9 +17,11 @@ import static com.github.tomakehurst.wiremock.client.WireMock.verify; import static com.sap.ai.sdk.orchestration.AzureFilterThreshold.ALLOW_SAFE; import static com.sap.ai.sdk.orchestration.AzureFilterThreshold.ALLOW_SAFE_LOW_MEDIUM; +import static com.sap.ai.sdk.orchestration.OrchestrationAiModel.GEMINI_2_5_FLASH; import static com.sap.ai.sdk.orchestration.OrchestrationAiModel.GPT_4O; import static com.sap.ai.sdk.orchestration.OrchestrationAiModel.GPT_4O_MINI; import static com.sap.ai.sdk.orchestration.OrchestrationAiModel.Parameter.*; +import static com.sap.ai.sdk.orchestration.OrchestrationTemplateReference.ScopeEnum.RESOURCE_GROUP; import static com.sap.ai.sdk.orchestration.model.AzureThreshold.NUMBER_0; import static com.sap.ai.sdk.orchestration.model.AzureThreshold.NUMBER_4; import static com.sap.ai.sdk.orchestration.model.AzureThreshold.NUMBER_6; @@ -1259,7 +1261,7 @@ void testResponseFormatText() throws IOException { } @Test - void testTemplateFromPromptRegistryById() throws IOException { + void testTemplateFromPromptRegistryByIdTenant() throws IOException { { stubFor( post(anyUrl()) @@ -1285,7 +1287,44 @@ void testTemplateFromPromptRegistryById() throws IOException { } @Test - void testTemplateFromPromptRegistryByScenario() throws IOException { + void testTemplateFromPromptRegistryByIdResourceGroup() throws IOException { + { + stubFor( + post(anyUrl()) + .willReturn( + aResponse() + .withBodyFile("templateReferenceResourceGroupResponse.json") + .withHeader("Content-Type", "application/json"))); + + var template = + TemplateConfig.reference() + .byId("8bf72116-11ab-41bb-8933-8be56f59cb67") + .withScope(RESOURCE_GROUP); + var config = + new OrchestrationModuleConfig() + .withLlmConfig(GEMINI_2_5_FLASH.withParam(TEMPERATURE, 0.0)); + var configWithTemplate = config.withTemplateConfig(template); + + var inputParams = + Map.of( + "categories", + "Finance, Tech, Sports", + "inputExample", + "What's the latest news on the stock market?"); + var prompt = new OrchestrationPrompt(inputParams); + + final var response = client.chatCompletion(prompt, configWithTemplate); + assertThat(response.getContent()).startsWith("Finance"); + assertThat(response.getOriginalResponse().getIntermediateResults().getTemplating()) + .hasSize(2); + + final String request = fileLoaderStr.apply("templateReferenceResourceGroupByIdRequest.json"); + verify(postRequestedFor(anyUrl()).withRequestBody(equalToJson(request))); + } + } + + @Test + void testTemplateFromPromptRegistryByScenarioTenant() throws IOException { stubFor( post(anyUrl()) .willReturn( @@ -1307,6 +1346,42 @@ void testTemplateFromPromptRegistryByScenario() throws IOException { verify(postRequestedFor(anyUrl()).withRequestBody(equalToJson(request))); } + @Test + void testTemplateFromPromptRegistryByScenarioResourceGroup() throws IOException { + stubFor( + post(anyUrl()) + .willReturn( + aResponse() + .withBodyFile("templateReferenceResourceGroupResponse.json") + .withHeader("Content-Type", "application/json"))); + + var template = + TemplateConfig.reference() + .byScenario("categorization") + .name("example-prompt-template") + .version("0.0.1") + .withScope(RESOURCE_GROUP); + var config = + new OrchestrationModuleConfig().withLlmConfig(GEMINI_2_5_FLASH.withParam(TEMPERATURE, 0.0)); + var configWithTemplate = config.withTemplateConfig(template); + + var inputParams = + Map.of( + "categories", + "Finance, Tech, Sports", + "inputExample", + "What's the latest news on the stock market?"); + var prompt = new OrchestrationPrompt(inputParams); + + final var response = client.chatCompletion(prompt, configWithTemplate); + assertThat(response.getContent()).startsWith("Finance"); + assertThat(response.getOriginalResponse().getIntermediateResults().getTemplating()).hasSize(2); + + final String request = + fileLoaderStr.apply("templateReferenceResourceGroupByScenarioRequest.json"); + verify(postRequestedFor(anyUrl()).withRequestBody(equalToJson(request))); + } + @Test void testTemplateFromInput() throws IOException { stubFor( diff --git a/orchestration/src/test/resources/__files/templateReferenceResourceGroupResponse.json b/orchestration/src/test/resources/__files/templateReferenceResourceGroupResponse.json new file mode 100644 index 000000000..25ba4607a --- /dev/null +++ b/orchestration/src/test/resources/__files/templateReferenceResourceGroupResponse.json @@ -0,0 +1,69 @@ +{ + "request_id": "921f38b7-3434-9171-85df-27be1b7fca3c", + "intermediate_results": { + "templating": [ + { + "role": "system", + "content": "You classify input text into the two following categories: Finance, Tech, Sports" + }, + { + "content": "What's the latest news on the stock market?", + "role": "user" + } + ], + "llm": { + "id": "", + "object": "chat.completion", + "created": 1769424215, + "model": "gemini-2.5-flash", + "choices": [ + { + "index": 0, + "message": { + "role": "assistant", + "content": "Finance" + }, + "finish_reason": "stop" + } + ], + "usage": { + "completion_tokens": 154, + "prompt_tokens": 26, + "total_tokens": 180, + "prompt_tokens_details": { + "cached_tokens": 0 + }, + "completion_tokens_details": { + "reasoning_tokens": 153 + } + } + } + }, + "final_result": { + "id": "", + "object": "chat.completion", + "created": 1769424215, + "model": "gemini-2.5-flash", + "choices": [ + { + "index": 0, + "message": { + "role": "assistant", + "content": "Finance" + }, + "finish_reason": "stop" + } + ], + "usage": { + "completion_tokens": 154, + "prompt_tokens": 26, + "total_tokens": 180, + "prompt_tokens_details": { + "cached_tokens": 0 + }, + "completion_tokens_details": { + "reasoning_tokens": 153 + } + } + } +} diff --git a/orchestration/src/test/resources/templateReferenceResourceGroupByIdRequest.json b/orchestration/src/test/resources/templateReferenceResourceGroupByIdRequest.json new file mode 100644 index 000000000..b81565d24 --- /dev/null +++ b/orchestration/src/test/resources/templateReferenceResourceGroupByIdRequest.json @@ -0,0 +1,28 @@ +{ + "config": { + "modules": { + "prompt_templating": { + "prompt": { + "template_ref": { + "id": "8bf72116-11ab-41bb-8933-8be56f59cb67", + "scope": "resource_group" + } + }, + "model": { + "name": "gemini-2.5-flash", + "version": "latest", + "params": { + "temperature": 0.0 + }, + "timeout": 600, + "max_retries": 2 + } + } + } + }, + "placeholder_values": { + "categories": "Finance, Tech, Sports", + "inputExample": "What's the latest news on the stock market?" + }, + "messages_history": [] +} diff --git a/orchestration/src/test/resources/templateReferenceResourceGroupByScenarioRequest.json b/orchestration/src/test/resources/templateReferenceResourceGroupByScenarioRequest.json new file mode 100644 index 000000000..625c8ce70 --- /dev/null +++ b/orchestration/src/test/resources/templateReferenceResourceGroupByScenarioRequest.json @@ -0,0 +1,30 @@ +{ + "config": { + "modules": { + "prompt_templating": { + "prompt": { + "template_ref": { + "scenario": "categorization", + "name": "example-prompt-template", + "version": "0.0.1", + "scope": "resource_group" + } + }, + "model": { + "name": "gemini-2.5-flash", + "version": "latest", + "params": { + "temperature": 0.0 + }, + "timeout": 600, + "max_retries": 2 + } + } + } + }, + "placeholder_values": { + "categories": "Finance, Tech, Sports", + "inputExample": "What's the latest news on the stock market?" + }, + "messages_history": [] +} diff --git a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/controllers/OrchestrationController.java b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/controllers/OrchestrationController.java index b856a6264..805e5fc19 100644 --- a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/controllers/OrchestrationController.java +++ b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/controllers/OrchestrationController.java @@ -307,22 +307,48 @@ Object responseFormatJsonObject( return response.getContent(); } - @GetMapping("/templateFromPromptRegistryById") + @GetMapping("/templateFromPromptRegistryByIdTenant") @Nonnull - Object templateFromPromptRegistryById( + Object templateFromPromptRegistryByIdTenant( @RequestParam(value = "format", required = false) final String format) { - final var response = service.templateFromPromptRegistryById("cloud ERP systems"); + final var response = service.templateFromPromptRegistryByIdTenant("cloud ERP systems"); if ("json".equals(format)) { return response; } return response.getContent(); } - @GetMapping("/templateFromPromptRegistryByScenario") + @GetMapping("/templateFromPromptRegistryByIdResourceGroup") @Nonnull - Object templateFromPromptRegistryByScenario( + Object templateFromPromptRegistryByIdResourceGroup( @RequestParam(value = "format", required = false) final String format) { - final var response = service.templateFromPromptRegistryByScenario("cloud ERP systems"); + final var response = + service.templateFromPromptRegistryByIdResourceGroup( + "What's the latest news on the stock market?"); + if ("json".equals(format)) { + return response; + } + return response.getContent(); + } + + @GetMapping("/templateFromPromptRegistryByScenarioTenant") + @Nonnull + Object templateFromPromptRegistryByScenarioTenant( + @RequestParam(value = "format", required = false) final String format) { + final var response = service.templateFromPromptRegistryByScenarioTenant("cloud ERP systems"); + if ("json".equals(format)) { + return response; + } + return response.getContent(); + } + + @GetMapping("/templateFromPromptRegistryByScenarioResourceGroup") + @Nonnull + Object templateFromPromptRegistryByScenarioResourceGroup( + @RequestParam(value = "format", required = false) final String format) { + final var response = + service.templateFromPromptRegistryByScenarioResourceGroup( + "What's the latest news on the stock market?"); if ("json".equals(format)) { return response; } 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 c41a3e55a..54e081a8b 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 @@ -5,6 +5,7 @@ import static com.sap.ai.sdk.orchestration.OrchestrationAiModel.GPT_4O_MINI; import static com.sap.ai.sdk.orchestration.OrchestrationAiModel.Parameter.TEMPERATURE; import static com.sap.ai.sdk.orchestration.OrchestrationEmbeddingModel.TEXT_EMBEDDING_3_SMALL; +import static com.sap.ai.sdk.orchestration.OrchestrationTemplateReference.ScopeEnum.RESOURCE_GROUP; import com.fasterxml.jackson.annotation.JsonProperty; import com.sap.ai.sdk.core.AiCoreService; @@ -555,7 +556,8 @@ public OrchestrationChatResponse responseFormatText(@Nonnull final String word) * @return the assistant response object */ @Nonnull - public OrchestrationChatResponse templateFromPromptRegistryById(@Nonnull final String topic) { + public OrchestrationChatResponse templateFromPromptRegistryByIdTenant( + @Nonnull final String topic) { final var llmWithImageSupportConfig = new OrchestrationModuleConfig().withLlmConfig(GPT_4O_MINI); @@ -568,6 +570,35 @@ public OrchestrationChatResponse templateFromPromptRegistryById(@Nonnull final S return client.chatCompletion(prompt, configWithTemplate); } + /** + * Chat request to OpenAI through the Orchestration service using a template from the prompt + * registry. + * + * @link SAP + * AI Core: Orchestration - Templating + * @param inputExample the example to send to the assistant + * @return the assistant response object + */ + @Nonnull + public OrchestrationChatResponse templateFromPromptRegistryByIdResourceGroup( + @Nonnull final String inputExample) { + + final var destination = + new AiCoreService().getInferenceDestination("ai-sdk-java-e2e").forScenario("orchestration"); + final var clientWithResourceGroup = new OrchestrationClient(destination); + + val template = + TemplateConfig.reference() + .byId("8bf72116-11ab-41bb-8933-8be56f59cb67") + .withScope(RESOURCE_GROUP); + val configWithTemplate = config.withTemplateConfig(template); + + val inputParams = Map.of("categories", "Finance, Tech, Sports", "inputExample", inputExample); + val prompt = new OrchestrationPrompt(inputParams); + + return clientWithResourceGroup.chatCompletion(prompt, configWithTemplate); + } + /** * Chat request to OpenAI through the Orchestration service using a template from the prompt * registry. @@ -578,7 +609,7 @@ public OrchestrationChatResponse templateFromPromptRegistryById(@Nonnull final S * @return the assistant response object */ @Nonnull - public OrchestrationChatResponse templateFromPromptRegistryByScenario( + public OrchestrationChatResponse templateFromPromptRegistryByScenarioTenant( @Nonnull final String topic) { val template = TemplateConfig.reference().byScenario("test").name("test").version("0.0.1"); val configWithTemplate = config.withTemplateConfig(template); @@ -589,6 +620,36 @@ public OrchestrationChatResponse templateFromPromptRegistryByScenario( return client.chatCompletion(prompt, configWithTemplate); } + /** + * Chat request to OpenAI through the Orchestration service using a template from the prompt + * registry. + * + * @link SAP + * AI Core: Orchestration - Templating + * @param inputExample the example to send to the assistant + * @return the assistant response object + */ + @Nonnull + public OrchestrationChatResponse templateFromPromptRegistryByScenarioResourceGroup( + @Nonnull final String inputExample) { + final var destination = + new AiCoreService().getInferenceDestination("ai-sdk-java-e2e").forScenario("orchestration"); + final var clientWithResourceGroup = new OrchestrationClient(destination); + + val template = + TemplateConfig.reference() + .byScenario("categorization") + .name("example-prompt-template") + .version("0.0.1") + .withScope(RESOURCE_GROUP); + val configWithTemplate = config.withTemplateConfig(template); + + val inputParams = Map.of("categories", "Finance, Tech, Sports", "inputExample", inputExample); + val prompt = new OrchestrationPrompt(inputParams); + + return clientWithResourceGroup.chatCompletion(prompt, configWithTemplate); + } + /** * Chat request to an LLM through the Orchestration service using a local template file. * 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 491c7f912..73f8afdc6 100644 --- a/sample-code/spring-app/src/main/resources/static/index.html +++ b/sample-code/spring-app/src/main/resources/static/index.html @@ -509,12 +509,12 @@

Orchestration

  • - Chat request to an LLM through the Orchestration service + Chat request to an LLM through the Orchestration service in tenant scope using a template from the prompt registry identified by ID.
    @@ -523,12 +523,42 @@

    Orchestration

  • - Chat request to an LLM through the Orchestration service + Chat request to an LLM through the Orchestration service in resource group + scope + using a template from the prompt registry identified by + ID. +
    +
    +
  • +
  • +
    + +
    + Chat request to an LLM through the Orchestration service in tenant scope + using a template from the prompt registry identified by + Scenario, name, and version. +
    +
    +
  • +
  • +
    + +
    + Chat request to an LLM through the Orchestration service in resource group + scope using a template from the prompt registry identified by Scenario, name, and version.
    diff --git a/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/OrchestrationTest.java b/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/OrchestrationTest.java index f94dd0e25..e2ed9b6ab 100644 --- a/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/OrchestrationTest.java +++ b/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/OrchestrationTest.java @@ -401,16 +401,41 @@ void testResponseFormatText() { } @Test - void testTemplateFromPromptRegistryById() { - val result = service.templateFromPromptRegistryById("Cloud ERP systems").getOriginalResponse(); + void testTemplateFromPromptRegistryByIdTenant() { + val result = + service.templateFromPromptRegistryByIdTenant("Cloud ERP systems").getOriginalResponse(); + val choices = (result.getFinalResult()).getChoices(); + assertThat(choices.get(0).getMessage().getContent()).isNotEmpty(); + } + + @Test + void testTemplateFromPromptRegistryByIdResourceGroup() { + val result = + service + .templateFromPromptRegistryByIdResourceGroup( + "What's the latest news on the stock market?") + .getOriginalResponse(); val choices = (result.getFinalResult()).getChoices(); assertThat(choices.get(0).getMessage().getContent()).isNotEmpty(); } @Test - void testTemplateFromPromptRegistryByScenario() { + void testTemplateFromPromptRegistryByScenarioTenant() { val result = - service.templateFromPromptRegistryByScenario("Cloud ERP systems").getOriginalResponse(); + service + .templateFromPromptRegistryByScenarioTenant("Cloud ERP systems") + .getOriginalResponse(); + val choices = (result.getFinalResult()).getChoices(); + assertThat(choices.get(0).getMessage().getContent()).isNotEmpty(); + } + + @Test + void testTemplateFromPromptRegistryByScenarioResourceGroup() { + val result = + service + .templateFromPromptRegistryByScenarioResourceGroup( + "What's the latest news on the stock market?") + .getOriginalResponse(); val choices = (result.getFinalResult()).getChoices(); assertThat(choices.get(0).getMessage().getContent()).isNotEmpty(); }