diff --git a/orchestration/pom.xml b/orchestration/pom.xml index abcc51494..be999631d 100644 --- a/orchestration/pom.xml +++ b/orchestration/pom.xml @@ -37,9 +37,9 @@ ${project.basedir}/../ 82% - 94% - 93% - 75% + 93% + 92% + 77% 94% 100% diff --git a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/ConfigToRequestTransformer.java b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/ConfigToRequestTransformer.java index 4806489ce..c1b41d795 100644 --- a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/ConfigToRequestTransformer.java +++ b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/ConfigToRequestTransformer.java @@ -81,6 +81,7 @@ static PromptTemplatingModuleConfigPrompt toTemplateModuleConfig( * In this case, the request will fail, since the templating module will try to resolve the parameter. * To be fixed with https://github.tools.sap/AI/llm-orchestration/issues/662 */ + if (config instanceof TemplateRef) { return config; } @@ -249,4 +250,25 @@ static CompletionPostRequest fromReferenceToCompletionPostRequest( return request; } } + + @Nonnull + static CompletionRequestConfiguration fromTemplateRefToCompletionPostRequest( + @Nonnull final OrchestrationModuleConfig configWithRef) { + final OrchestrationTemplateReference templateRef = configWithRef.getTemplateRef(); + final var messageHistory = + templateRef.getMessagesHistory().stream().map(Message::createChatMessage).toList(); + final var placeholders = templateRef.getTemplateParameters(); + + final OrchestrationModuleConfig inner = + configWithRef.withTemplateConfig(templateRef.toLowLevel()); + + val requestConfig = + OrchestrationConfig.create().modules(toModuleConfigs(inner)).stream( + configWithRef.getGlobalStreamOptions()); + + return CompletionRequestConfiguration.create() + .config(requestConfig) + .placeholderValues(placeholders) + .messagesHistory(messageHistory); + } } diff --git a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationClient.java b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationClient.java index 955a04def..69eb20fa4 100644 --- a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationClient.java +++ b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationClient.java @@ -192,6 +192,24 @@ public OrchestrationChatResponse chatCompletionUsingReference( return new OrchestrationChatResponse(response); } + /** + * Generate a completion using a module configuration containing a template reference Per-request + * history and parameters must be set on the template reference via {@link + * OrchestrationTemplateReference#withMessageHistory} and {@link + * OrchestrationTemplateReference#withTemplateParameters}. + * + * @param config A module configuration wrapping an {@link OrchestrationTemplateReference}. + * @return The completion output. + * @since 1.26.0 + */ + @Nonnull + public OrchestrationChatResponse chatCompletionUsingTemplateRef( + @Nonnull final OrchestrationModuleConfig config) { + val request = ConfigToRequestTransformer.fromTemplateRefToCompletionPostRequest(config); + val response = executeRequest(request); + return new OrchestrationChatResponse(response); + } + /** * Perform a request to the orchestration service using a module configuration provided as JSON * string. This can be useful when building a configuration in the AI Launchpad UI and exporting diff --git a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationModuleConfig.java b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationModuleConfig.java index 82f4c6754..668e2c826 100644 --- a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationModuleConfig.java +++ b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationModuleConfig.java @@ -136,6 +136,8 @@ public class OrchestrationModuleConfig { @Nullable SAPDocumentTranslationOutput outputTranslationConfig; + @Nullable OrchestrationTemplateReference templateRef; + /** Configuration of optional streaming options for output filtering. */ @With(AccessLevel.NONE) // may be exposed to public in the future @Getter(AccessLevel.PACKAGE) @@ -304,6 +306,7 @@ OrchestrationModuleConfig withOutputFilteringStreamOptions( this.groundingConfig, this.inputTranslationConfig, this.outputTranslationConfig, + this.templateRef, outputFilteringStreamOptions, this.globalStreamOptions); } 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 62fd0e067..922bb9677 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 @@ -5,10 +5,13 @@ 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 java.util.List; +import java.util.Map; import javax.annotation.Nonnull; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; +import lombok.Getter; import lombok.Value; import lombok.With; @@ -28,6 +31,46 @@ public class OrchestrationTemplateReference extends TemplateConfig { /** The scope of the template reference. */ @With @Nonnull ScopeEnum scope; + @Getter(AccessLevel.PACKAGE) + @Nonnull + List messagesHistory; + + @Getter(AccessLevel.PACKAGE) + @Nonnull + Map templateParameters; + + /** Build a template reference with scope only. */ + OrchestrationTemplateReference( + @Nonnull final TemplateRefTemplateRef reference, @Nonnull final ScopeEnum scope) { + this(reference, scope, List.of(), Map.of()); + } + + /** + * Set the chat history. + * + * @param messagesHistory The chat history to set. + * @return A new instance with the specified chat history. + */ + @Nonnull + public OrchestrationTemplateReference withMessageHistory( + @Nonnull final List messagesHistory) { + return new OrchestrationTemplateReference( + reference, scope, messagesHistory, templateParameters); + } + + /** + * Set the template parameters. + * + * @param templateParameters The template parameters to set. + * @return A new instance with the specified template parameters. + */ + @Nonnull + public OrchestrationTemplateReference withTemplateParameters( + @Nonnull final Map templateParameters) { + return new OrchestrationTemplateReference( + reference, scope, messagesHistory, templateParameters); + } + /** * Create a low-level representation of the template. * 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..81b487bec 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 @@ -695,13 +695,14 @@ public OrchestrationChatResponse templateFromPromptRegistryByIdTenant( @Nonnull final String topic) { final var llmWithImageSupportConfig = new OrchestrationModuleConfig().withLlmConfig(GPT_5_MINI); - val template = TemplateConfig.reference().byId("21cb1358-0bf1-4f43-870b-00f14d0f9f16"); - val configWithTemplate = llmWithImageSupportConfig.withTemplateConfig(template); - val inputParams = Map.of("language", "Italian", "input", topic); - val prompt = new OrchestrationPrompt(inputParams); + val template = + TemplateConfig.reference() + .byId("21cb1358-0bf1-4f43-870b-00f14d0f9f16") + .withTemplateParameters(inputParams); + val configWithTemplate = llmWithImageSupportConfig.withTemplateConfig(template); - return client.chatCompletion(prompt, configWithTemplate); + return client.chatCompletionUsingTemplateRef(configWithTemplate); } /** @@ -720,16 +721,15 @@ public OrchestrationChatResponse templateFromPromptRegistryByIdResourceGroup( final var clientWithResourceGroup = client.withResourceGroup("ai-sdk-java-e2e", "orchestration"); + val inputParams = Map.of("categories", "Finance, Tech, Sports", "inputExample", inputExample); val template = TemplateConfig.reference() .byId("8bf72116-11ab-41bb-8933-8be56f59cb67") - .withScope(RESOURCE_GROUP); + .withScope(RESOURCE_GROUP) + .withTemplateParameters(inputParams); 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); + return clientWithResourceGroup.chatCompletionUsingTemplateRef(configWithTemplate); } /** @@ -744,13 +744,16 @@ public OrchestrationChatResponse templateFromPromptRegistryByIdResourceGroup( @Nonnull public OrchestrationChatResponse templateFromPromptRegistryByScenarioTenant( @Nonnull final String topic) { - val template = TemplateConfig.reference().byScenario("test").name("test").version("0.0.1"); - val configWithTemplate = config.withTemplateConfig(template); - val inputParams = Map.of("language", "Italian", "input", topic); - val prompt = new OrchestrationPrompt(inputParams); + val template = + TemplateConfig.reference() + .byScenario("test") + .name("test") + .version("0.0.1") + .withTemplateParameters(inputParams); + val configWithTemplate = config.withTemplateConfig(template); - return client.chatCompletion(prompt, configWithTemplate); + return client.chatCompletionUsingTemplateRef(configWithTemplate); } /** @@ -769,18 +772,17 @@ public OrchestrationChatResponse templateFromPromptRegistryByScenarioResourceGro final var clientWithResourceGroup = client.withResourceGroup("ai-sdk-java-e2e", "orchestration"); + val inputParams = Map.of("categories", "Finance, Tech, Sports", "inputExample", inputExample); val template = TemplateConfig.reference() .byScenario("categorization") .name("example-prompt-template") .version("0.0.1") - .withScope(RESOURCE_GROUP); + .withScope(RESOURCE_GROUP) + .withTemplateParameters(inputParams); 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); + return clientWithResourceGroup.chatCompletionUsingTemplateRef(configWithTemplate); } /**