Skip to content

Commit e8a95fc

Browse files
committed
All hail the new orchestration client
1 parent 8be7991 commit e8a95fc

34 files changed

+1886
-491
lines changed

orchestration/pom.xml

Lines changed: 51 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -36,32 +36,66 @@
3636
<dependencies>
3737
<!-- scope "compile" -->
3838
<dependency>
39-
<groupId>com.sap.cloud.sdk.datamodel</groupId>
40-
<artifactId>openapi-core</artifactId>
39+
<groupId>com.sap.ai.sdk</groupId>
40+
<artifactId>core</artifactId>
4141
</dependency>
42+
4243
<dependency>
43-
<groupId>org.springframework</groupId>
44-
<artifactId>spring-core</artifactId>
44+
<groupId>com.sap.cloud.sdk.cloudplatform</groupId>
45+
<artifactId>cloudplatform-connectivity</artifactId>
4546
</dependency>
4647
<dependency>
47-
<groupId>org.springframework</groupId>
48-
<artifactId>spring-web</artifactId>
48+
<groupId>com.sap.cloud.sdk.cloudplatform</groupId>
49+
<artifactId>connectivity-apache-httpclient5</artifactId>
4950
</dependency>
51+
5052
<dependency>
51-
<groupId>com.sap.cloud.sdk.cloudplatform</groupId>
52-
<artifactId>cloudplatform-connectivity</artifactId>
53+
<groupId>org.apache.httpcomponents.core5</groupId>
54+
<artifactId>httpcore5</artifactId>
55+
</dependency>
56+
<dependency>
57+
<groupId>org.apache.httpcomponents.client5</groupId>
58+
<artifactId>httpclient5</artifactId>
5359
</dependency>
5460
<dependency>
5561
<groupId>com.google.code.findbugs</groupId>
5662
<artifactId>jsr305</artifactId>
5763
</dependency>
5864
<dependency>
59-
<groupId>com.google.guava</groupId>
60-
<artifactId>guava</artifactId>
65+
<groupId>com.fasterxml.jackson.core</groupId>
66+
<artifactId>jackson-annotations</artifactId>
6167
</dependency>
6268
<dependency>
6369
<groupId>com.fasterxml.jackson.core</groupId>
64-
<artifactId>jackson-annotations</artifactId>
70+
<artifactId>jackson-core</artifactId>
71+
</dependency>
72+
<dependency>
73+
<groupId>com.fasterxml.jackson.core</groupId>
74+
<artifactId>jackson-databind</artifactId>
75+
</dependency>
76+
<dependency>
77+
<groupId>com.fasterxml.jackson.datatype</groupId>
78+
<artifactId>jackson-datatype-jsr310</artifactId>
79+
</dependency>
80+
<dependency>
81+
<groupId>io.vavr</groupId>
82+
<artifactId>vavr</artifactId>
83+
</dependency>
84+
<dependency>
85+
<groupId>org.slf4j</groupId>
86+
<artifactId>slf4j-api</artifactId>
87+
</dependency>
88+
89+
<!-- TODO: only needed for JsonObjectMapperBuilder, maybe we can use Jackson natively to avoid this dependency -->
90+
<dependency>
91+
<groupId>org.springframework</groupId>
92+
<artifactId>spring-web</artifactId>
93+
</dependency>
94+
<!-- scope "provided" -->
95+
<dependency>
96+
<groupId>org.projectlombok</groupId>
97+
<artifactId>lombok</artifactId>
98+
<scope>provided</scope>
6599
</dependency>
66100
<!-- scope "test" -->
67101
<dependency>
@@ -74,54 +108,31 @@
74108
<artifactId>wiremock</artifactId>
75109
<scope>test</scope>
76110
</dependency>
77-
<dependency>
78-
<groupId>org.apache.httpcomponents.core5</groupId>
79-
<artifactId>httpcore5</artifactId>
80-
<scope>test</scope>
81-
</dependency>
82111
<dependency>
83112
<groupId>org.assertj</groupId>
84113
<artifactId>assertj-core</artifactId>
85114
<scope>test</scope>
86115
</dependency>
87116
<dependency>
88-
<groupId>com.sap.ai.sdk</groupId>
89-
<artifactId>core</artifactId>
117+
<groupId>org.mockito</groupId>
118+
<artifactId>mockito-core</artifactId>
90119
<scope>test</scope>
91120
</dependency>
92121
</dependencies>
93122

94123
<build>
95124
<plugins>
96-
<plugin>
97-
<artifactId>maven-clean-plugin</artifactId>
98-
<configuration>
99-
<filesets>
100-
<fileset>
101-
<directory>${project.basedir}/src/main/java/com/sap/ai/sdk/orchestration/client</directory>
102-
<includes>
103-
<include>**/*</include>
104-
</includes>
105-
</fileset>
106-
</filesets>
107-
</configuration>
108-
<executions>
109-
<execution>
110-
<id>delete-orchestration-generated-client</id>
111-
</execution>
112-
</executions>
113-
</plugin>
114125
<plugin>
115126
<groupId>com.sap.cloud.sdk.datamodel</groupId>
116127
<artifactId>openapi-generator-maven-plugin</artifactId>
117128
<configuration>
129+
<skip>true</skip>
130+
<!-- skip automatic generation until we can omit API classes from code generation -->
118131
<outputDirectory>${project.basedir}/src/main/java</outputDirectory>
119-
<apiMaturity>released</apiMaturity>
132+
<apiMaturity>beta</apiMaturity>
120133
<enableOneOfAnyOfGeneration>true</enableOneOfAnyOfGeneration>
121134
<compileScope>COMPILE</compileScope>
122-
<!-- Do not delete the output directory because it contains non-generated code -->
123-
<!-- The generated client is instead deleted by the maven-clean-plugin here above -->
124-
<deleteOutputDirectory>false</deleteOutputDirectory>
135+
<deleteOutputDirectory>true</deleteOutputDirectory>
125136
</configuration>
126137
<executions>
127138
<execution>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.sap.ai.sdk.orchestration;
2+
3+
import javax.annotation.Nonnull;
4+
5+
public record AssistantMessage(@Nonnull String content) implements Message {
6+
@Nonnull
7+
@Override
8+
public String type() {
9+
return "assistant";
10+
}
11+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package com.sap.ai.sdk.orchestration;
2+
3+
import static com.sap.ai.sdk.orchestration.client.model.AzureThreshold.fromValue;
4+
5+
import com.sap.ai.sdk.orchestration.client.model.AzureContentSafety;
6+
import javax.annotation.Nonnull;
7+
import javax.annotation.Nullable;
8+
import lombok.Data;
9+
import lombok.RequiredArgsConstructor;
10+
import lombok.experimental.Accessors;
11+
import lombok.val;
12+
13+
@Data
14+
@Accessors(fluent = true)
15+
public final class AzureContentFilter implements ContentFilter {
16+
@Nullable private Sensitivity hate;
17+
@Nullable private Sensitivity selfHarm;
18+
@Nullable private Sensitivity sexual;
19+
@Nullable private Sensitivity violence;
20+
21+
@RequiredArgsConstructor
22+
public enum Sensitivity {
23+
HIGH(0),
24+
MEDIUM(2),
25+
LOW(4);
26+
27+
private final int value;
28+
}
29+
30+
@Nonnull
31+
com.sap.ai.sdk.orchestration.client.model.FilterConfig toFilterConfigDTO() {
32+
val dto = AzureContentSafety.create();
33+
if (hate != null) {
34+
dto.hate(fromValue(hate.value));
35+
}
36+
if (selfHarm != null) {
37+
dto.selfHarm(fromValue(selfHarm.value));
38+
}
39+
if (sexual != null) {
40+
dto.sexual(fromValue(sexual.value));
41+
}
42+
if (violence != null) {
43+
dto.violence(fromValue(violence.value));
44+
}
45+
return com.sap.ai.sdk.orchestration.client.model.FilterConfig.create()
46+
.type(com.sap.ai.sdk.orchestration.client.model.FilterConfig.TypeEnum.AZURE_CONTENT_SAFETY)
47+
.config(dto);
48+
}
49+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package com.sap.ai.sdk.orchestration;
2+
3+
public sealed interface ContentFilter permits AzureContentFilter {}
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
package com.sap.ai.sdk.orchestration;
2+
3+
import com.sap.ai.sdk.core.AiModel;
4+
import io.vavr.control.Option;
5+
import javax.annotation.Nonnull;
6+
import lombok.AccessLevel;
7+
import lombok.Data;
8+
import lombok.EqualsAndHashCode;
9+
import lombok.Getter;
10+
import lombok.RequiredArgsConstructor;
11+
import lombok.Setter;
12+
import lombok.ToString;
13+
14+
@Data
15+
@Setter(AccessLevel.PRIVATE)
16+
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
17+
public class DefaultOrchestrationConfig<T extends OrchestrationConfig<T>>
18+
implements OrchestrationConfig<T> {
19+
20+
@Nonnull private Option<AiModel> llmConfig = Option.none();
21+
@Nonnull private Option<TemplateConfig> template = Option.none();
22+
@Nonnull private Option<MaskingConfig> maskingConfig = Option.none();
23+
@Nonnull private Option<ContentFilter> inputContentFilter = Option.none();
24+
@Nonnull private Option<ContentFilter> outputContentFilter = Option.none();
25+
26+
@EqualsAndHashCode.Exclude
27+
@ToString.Exclude
28+
@Getter(AccessLevel.NONE)
29+
@Nonnull
30+
private final T wrapper;
31+
32+
@SuppressWarnings("unchecked")
33+
private DefaultOrchestrationConfig() {
34+
wrapper = (T) this;
35+
}
36+
37+
/**
38+
* Create a new instance of {@link DefaultOrchestrationConfig} to delegate to. This is useful when
39+
* exposing the {@link OrchestrationConfig} in other objects, without re-implementing it. To
40+
* maintain fluent API usage, the given wrapper object will be returned by the fluent methods,
41+
* instead of this instance.
42+
*
43+
* @param wrapper The wrapper that delegates to this object.
44+
* @param <T> The type of the wrapper object.
45+
* @return The new instance.
46+
* @see #standalone()
47+
*/
48+
@Nonnull
49+
public static <T extends OrchestrationConfig<T>> DefaultOrchestrationConfig<T> asDelegateFor(
50+
@Nonnull final T wrapper) {
51+
return new DefaultOrchestrationConfig<>(wrapper);
52+
}
53+
54+
/**
55+
* Create an implementation without any object delegating to it. The fluent API will return this
56+
* object itself.
57+
*
58+
* @return The new instance.
59+
* @see #asDelegateFor(OrchestrationConfig)
60+
*/
61+
@Nonnull
62+
public static DefaultOrchestrationConfig<?> standalone() {
63+
return new DefaultOrchestrationConfig<>();
64+
}
65+
66+
@Nonnull
67+
@Override
68+
public T withLlmConfig(@Nonnull final AiModel llm) {
69+
this.llmConfig = Option.some(llm);
70+
return wrapper;
71+
}
72+
73+
@Nonnull
74+
@Override
75+
public T withTemplate(@Nonnull final TemplateConfig templateConfig) {
76+
this.template = Option.some(templateConfig);
77+
return wrapper;
78+
}
79+
80+
@Nonnull
81+
@Override
82+
public T withMaskingConfig(@Nonnull final MaskingConfig maskingConfig) {
83+
this.maskingConfig = Option.some(maskingConfig);
84+
return wrapper;
85+
}
86+
87+
@Nonnull
88+
@Override
89+
public T withInputContentFilter(@Nonnull final ContentFilter filter) {
90+
this.inputContentFilter = Option.some(filter);
91+
return wrapper;
92+
}
93+
94+
@Nonnull
95+
@Override
96+
public T withOutputContentFilter(@Nonnull final ContentFilter filter) {
97+
this.outputContentFilter = Option.some(filter);
98+
return wrapper;
99+
}
100+
101+
/**
102+
* Copy the configuration into the given target configuration. The copy is
103+
* <strong>shallow</strong> and does <strong>not override</strong> any existing configuration.
104+
*
105+
* <p>This has two main use cases:
106+
*
107+
* <ol>
108+
* <li>Duplicating a config
109+
* <li>Applying defaults to a config
110+
* </ol>
111+
*
112+
* @param source The source configuration to copy from.
113+
* @return This (delegate) object.
114+
*/
115+
@Nonnull
116+
public DefaultOrchestrationConfig<T> copyFrom(@Nonnull final OrchestrationConfig<?> source) {
117+
llmConfig.orElse(source::getLlmConfig).forEach(this::withLlmConfig);
118+
template.orElse(source::getTemplate).forEach(this::withTemplate);
119+
maskingConfig.orElse(source::getMaskingConfig).forEach(this::withMaskingConfig);
120+
inputContentFilter.orElse(source::getInputContentFilter).forEach(this::withInputContentFilter);
121+
outputContentFilter
122+
.orElse(source::getOutputContentFilter)
123+
.forEach(this::withOutputContentFilter);
124+
return this;
125+
}
126+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package com.sap.ai.sdk.orchestration;
2+
3+
import static com.sap.ai.sdk.orchestration.client.model.MaskingProviderConfig.MethodEnum.ANONYMIZATION;
4+
import static com.sap.ai.sdk.orchestration.client.model.MaskingProviderConfig.MethodEnum.PSEUDONYMIZATION;
5+
import static com.sap.ai.sdk.orchestration.client.model.MaskingProviderConfig.TypeEnum.SAP_DATA_PRIVACY_INTEGRATION;
6+
7+
import com.sap.ai.sdk.orchestration.client.model.DPIEntities;
8+
import com.sap.ai.sdk.orchestration.client.model.DPIEntityConfig;
9+
import com.sap.ai.sdk.orchestration.client.model.MaskingProviderConfig;
10+
import java.util.ArrayList;
11+
import java.util.Arrays;
12+
import java.util.List;
13+
import javax.annotation.Nonnull;
14+
import javax.annotation.Nullable;
15+
import lombok.AccessLevel;
16+
import lombok.RequiredArgsConstructor;
17+
import lombok.Value;
18+
import lombok.val;
19+
20+
@Value
21+
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
22+
public class DpiMaskingConfig implements MaskingConfig {
23+
@Nonnull MaskingProviderConfig.MethodEnum maskingMethod;
24+
@Nonnull List<DPIEntities> entities;
25+
26+
@Nonnull
27+
public static Builder anonymization() {
28+
return new DpiMaskingConfig.Builder(ANONYMIZATION);
29+
}
30+
31+
@Nonnull
32+
public static Builder pseudonymization() {
33+
return new DpiMaskingConfig.Builder(PSEUDONYMIZATION);
34+
}
35+
36+
@Nonnull
37+
MaskingProviderConfig toMaskingProviderDTO() {
38+
val entitiesDTO = entities.stream().map(it -> DPIEntityConfig.create().type(it)).toList();
39+
return MaskingProviderConfig.create()
40+
.type(SAP_DATA_PRIVACY_INTEGRATION)
41+
.method(maskingMethod)
42+
.entities(entitiesDTO);
43+
}
44+
45+
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
46+
public static class Builder {
47+
private final MaskingProviderConfig.MethodEnum maskingMethod;
48+
49+
@Nonnull
50+
public DpiMaskingConfig withEntities(
51+
@Nonnull final DPIEntities entity, @Nullable final DPIEntities... entities) {
52+
val entitiesList = new ArrayList<DPIEntities>();
53+
entitiesList.add(entity);
54+
if (entities != null) {
55+
entitiesList.addAll(Arrays.asList(entities));
56+
}
57+
return new DpiMaskingConfig(maskingMethod, entitiesList);
58+
}
59+
}
60+
}

0 commit comments

Comments
 (0)