diff --git a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/DefaultOrchestrationConfig.java b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/DefaultOrchestrationConfig.java index cfb59635..2f12ae8f 100644 --- a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/DefaultOrchestrationConfig.java +++ b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/DefaultOrchestrationConfig.java @@ -26,95 +26,46 @@ public class DefaultOrchestrationConfig> @Nonnull private Option inputContentFilter = Option.none(); @Nonnull private Option outputContentFilter = Option.none(); - @Nonnull private final T wrapper; - - /** - * Create a new instance of {@link DefaultOrchestrationConfig} to delegate to. This is useful when - * exposing the {@link OrchestrationConfig} in other objects, without re-implementing it. To - * maintain fluent API usage, the given wrapper object will be returned by the fluent methods, - * instead of this instance. - * - * @param wrapper The wrapper that delegates to this object. - * @param The type of the wrapper object. - * @return The new instance. - * @see #standalone() - */ - public static > DefaultOrchestrationConfig asDelegateFor( - @Nonnull final T wrapper) { - return new DefaultOrchestrationConfig<>(wrapper); - } - - /** - * Create an implementation without any object delegating to it. The fluent API will return this - * object itself. - * - * @return The new instance. - * @see #asDelegateFor(OrchestrationConfig) - */ - static DefaultOrchestrationConfig standalone() { - return new DefaultOrchestrationConfig<>(); - } - + @Nonnull + @Override @SuppressWarnings("unchecked") - private DefaultOrchestrationConfig() { - this.wrapper = (T) this; - } - - private DefaultOrchestrationConfig(@Nonnull final T wrapper) { - this.wrapper = wrapper; + public T instance() { + return (T) this; } @Nonnull @Override public T withLlmConfig(@Nonnull final LLMModuleConfig llm) { this.llmConfig = Option.some(llm); - return wrapper; + return instance(); } @Nonnull @Override public T withTemplate(@Nonnull final TemplatingModuleConfig template) { this.template = Option.some(template); - return wrapper; + return instance(); } @Nonnull @Override public T withMaskingConfig(@Nonnull final DpiMaskingConfig maskingConfig) { this.maskingConfig = Option.some(maskingConfig); - return wrapper; + return instance(); } @Nonnull @Override public T withInputContentFilter(@Nonnull final FilterConfig filter) { this.inputContentFilter = Option.some(filter); - return wrapper; + return instance(); } @Nonnull @Override public T withOutputContentFilter(@Nonnull final FilterConfig filter) { this.outputContentFilter = Option.some(filter); - return wrapper; - } - - @Nonnull - static OrchestrationConfig fromConfigAndDefaults( - @Nonnull final OrchestrationConfig config, - @Nonnull final OrchestrationConfig defaults) { - - var copy = DefaultOrchestrationConfig.standalone(); - - copy.llmConfig = config.getLlmConfig().orElse(defaults::getLlmConfig); - copy.template = config.getTemplate().orElse(defaults::getTemplate); - copy.maskingConfig = config.getMaskingConfig().orElse(defaults::getMaskingConfig); - copy.inputContentFilter = - config.getInputContentFilter().orElse(defaults::getInputContentFilter); - copy.outputContentFilter = - config.getOutputContentFilter().orElse(defaults::getOutputContentFilter); - - return copy; + return instance(); } @Nonnull 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 a8d42813..c09a1b45 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 @@ -38,7 +38,7 @@ public class OrchestrationClient implements OrchestrationConfig clientConfig = - DefaultOrchestrationConfig.asDelegateFor(this); + new DefaultOrchestrationConfig<>(); @Nonnull private final HttpDestination destination; @@ -47,6 +47,12 @@ public OrchestrationClient() { this.destination = Core.getDestinationForDeployment("db1d64d9f06be467", "default").asHttp(); } + @Nonnull + @Override + public OrchestrationClient instance() { + return this; + } + /** * Generate a completion for the given user prompt. * diff --git a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationConfig.java b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationConfig.java index cfc30036..8d4cbec3 100644 --- a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationConfig.java +++ b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationConfig.java @@ -13,6 +13,9 @@ * @param Type of the specific implementation to make a fluent API possible. */ public interface OrchestrationConfig> { + @Nonnull + T instance(); + @Nonnull Option getLlmConfig(); @@ -42,4 +45,30 @@ public interface OrchestrationConfig> { @Nonnull T withOutputContentFilter(@Nonnull final FilterConfig filter); + + /** + * Copy the configuration into the given target configuration. The copy is + * shallow and does not override any existing configuration. + * + *

This has two main use cases: + * + *

    + *
  1. Duplicating a config + *
  2. Applying defaults to a config + *
+ * + * @param source The source configuration to copy from. + */ + default T copyFrom(@Nonnull final OrchestrationConfig source) { + getLlmConfig().orElse(source::getLlmConfig).forEach(this::withLlmConfig); + getTemplate().orElse(source::getTemplate).forEach(this::withTemplate); + getMaskingConfig().orElse(source::getMaskingConfig).forEach(this::withMaskingConfig); + getInputContentFilter() + .orElse(source::getInputContentFilter) + .forEach(this::withInputContentFilter); + getOutputContentFilter() + .orElse(source::getOutputContentFilter) + .forEach(this::withOutputContentFilter); + return instance(); + } } diff --git a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationPrompt.java b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationPrompt.java index 60fa6988..37b01b12 100644 --- a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationPrompt.java +++ b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationPrompt.java @@ -20,8 +20,13 @@ public class OrchestrationPrompt implements OrchestrationConfig delegate = - DefaultOrchestrationConfig.asDelegateFor(this); + DefaultOrchestrationConfig delegate = new DefaultOrchestrationConfig<>(); + + @Nonnull + @Override + public OrchestrationPrompt instance() { + return this; + } public OrchestrationPrompt(@Nonnull final String message) { this(List.of(ChatMessage.create().role("user").content(message)), Map.of()); @@ -37,7 +42,8 @@ public OrchestrationPrompt(@Nonnull final Map inputParams) { @Nonnull ModuleConfigs toModuleConfigDTO(@Nonnull final OrchestrationConfig defaults) { - var config = DefaultOrchestrationConfig.fromConfigAndDefaults(delegate, defaults); + // duplicate the prompt config so it isn't modified, to make sure this prompt can be reused + var config = new DefaultOrchestrationConfig<>().copyFrom(this).copyFrom(defaults); return DefaultOrchestrationConfig.toModuleConfigDTO(config, messages); } } diff --git a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/spring/OrchestrationChatOptions.java b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/spring/OrchestrationChatOptions.java index 53e5103c..981513b4 100644 --- a/orchestration/src/main/java/com/sap/ai/sdk/orchestration/spring/OrchestrationChatOptions.java +++ b/orchestration/src/main/java/com/sap/ai/sdk/orchestration/spring/OrchestrationChatOptions.java @@ -20,12 +20,18 @@ /** Configuration to be used for orchestration requests. */ @Data @Getter(AccessLevel.PACKAGE) -@Setter(AccessLevel.NONE) +@Setter(AccessLevel.PRIVATE) public class OrchestrationChatOptions implements ChatOptions, OrchestrationConfig { @Delegate @Nonnull private final DefaultOrchestrationConfig delegate = - DefaultOrchestrationConfig.asDelegateFor(this); + new DefaultOrchestrationConfig<>(); + + @Nonnull + @Override + public OrchestrationChatOptions instance() { + return this; + } @Getter(AccessLevel.PUBLIC) @Nonnull @@ -44,6 +50,7 @@ public OrchestrationChatOptions withTemplate(@Nonnull final List templa return this; } + @Nonnull static List toChatMessages(@Nonnull final List messages) { return messages.stream() .map(m -> ChatMessage.create().role(m.getMessageType().getValue()).content(m.getContent())) @@ -106,6 +113,8 @@ public Double getTopP() { @Override public OrchestrationChatOptions copy() { + var copy = new OrchestrationChatOptions(); + // TODO: implement, needed for chat memory apparently throw new RuntimeException("Not implemented"); }