Skip to content

Commit bea4d49

Browse files
committed
Merge branch 'refs/heads/main' into openapi-generator-orchestration
# Conflicts: # orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationClient.java # orchestration/src/test/java/com/sap/ai/sdk/orchestration/OrchestrationUnitTest.java # sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/controllers/OrchestrationController.java # sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/OrchestrationTest.java
2 parents 4750bb8 + 0c4d933 commit bea4d49

File tree

20 files changed

+532
-327
lines changed

20 files changed

+532
-327
lines changed

README.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ For more detailed information and advanced usage, please refer to the following:
251251

252252
## FAQs
253253

254-
### How to add a custom header to AI Core requests?
254+
### _"How to add a custom header to AI Core requests?"_
255255

256256
To add a header to AI Core requests, use the following code:
257257

@@ -262,7 +262,15 @@ DeploymentApi api = new DeploymentApi(client);
262262

263263
For more customization, creating a [HeaderProvider](https://sap.github.io/cloud-sdk/docs/java/features/connectivity/http-destinations#about-headerproviders) is also possible.
264264

265-
### More Examples
265+
266+
### _"There's a vulnerability warning `CVE-2021-41251`?"_
267+
268+
This is a known false-positive finding.
269+
Depending on the tooling any product called "SAP Cloud SDK" or similar with a low version number may be marked as vulnerable, incorrectly.
270+
Please consider suppressing the warning, [as we do](https://github.com/SAP/ai-sdk-java/blob/main/.pipeline/dependency-check-suppression.xml).
271+
272+
273+
### _"Are there any example projects?"_
266274

267275
Explore example applications and code snippets:
268276

core/src/main/java/com/sap/ai/sdk/core/DeploymentCache.java

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -135,13 +135,9 @@ protected static boolean isDeploymentOfModel(
135135
if (resources == null) {
136136
return false;
137137
}
138-
Object detailsObject = resources.getBackendDetails();
139-
// workaround for AIWDF-2124
138+
final Object detailsObject = resources.getBackendDetails();
140139
if (detailsObject == null) {
141-
if (!resources.getCustomFieldNames().contains("backend_details")) {
142-
return false;
143-
}
144-
detailsObject = resources.getCustomField("backend_details");
140+
return false;
145141
}
146142

147143
if (detailsObject instanceof Map<?, ?> details

core/src/test/java/com/sap/ai/sdk/core/CacheTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ public void isDeploymentOfModel() {
161161
.createdAt(OffsetDateTime.parse("2024-01-22T17:57:23+00:00"))
162162
.modifiedAt(OffsetDateTime.parse("2024-02-08T08:41:23+00:00"));
163163
deployment.setDetails(AiDeploymentDetails.create().resources(AiResourcesDetails.create()));
164-
deployment.getDetails().getResources().setCustomField("backend_details", model);
164+
deployment.getDetails().getResources().setBackendDetails(model);
165165

166166
// Check if the deployment is of the target model
167167
assertThat(DeploymentCache.isDeploymentOfModel(gpt4AnyVersion, deployment)).isTrue();

core/src/test/java/com/sap/ai/sdk/core/client/DeploymentUnitTest.java

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,7 @@ void getDeployments() {
6262
"ttl": null,
6363
"details": {
6464
"scaling": {
65-
"backendDetails": {},
66-
"backend_details": {}
65+
"backendDetails": {}
6766
},
6867
"resources": {
6968
"backendDetails": {
@@ -104,7 +103,6 @@ void getDeployments() {
104103
assertThat(deployment.getDeploymentUrl())
105104
.isEqualTo(
106105
"https://api.ai.intprod-eu12.eu-central-1.aws.ml.hana.ondemand.com/v2/inference/deployments/d889e3a61050c085");
107-
// Response contains key "backend_details" while spec (mistakenly) defines key "backendDetails".
108106
val expected = Map.of("model", Map.of("name", "gpt-4-32k", "version", "latest"));
109107
assertThat(deployment.getDetails().getResources().getBackendDetails()).isEqualTo(expected);
110108
assertThat(deployment.getDetails().getScaling().getBackendDetails()).isEqualTo(Map.of());
@@ -243,12 +241,12 @@ void getDeploymentById() {
243241
"deploymentUrl": "https://api.ai.intprod-eu12.eu-central-1.aws.ml.hana.ondemand.com/v2/inference/deployments/db1d64d9f06be467",
244242
"details": {
245243
"resources": {
246-
"backend_details": {},
247-
"backendDetails": {}
244+
"backendDetails": {},
245+
"backend_details": {}
248246
},
249247
"scaling": {
250-
"backend_details": {},
251-
"backendDetails": {}
248+
"backendDetails": {},
249+
"backend_details": {}
252250
}
253251
},
254252
"id": "db1d64d9f06be467",
@@ -272,8 +270,12 @@ void getDeploymentById() {
272270
assertThat(deployment.getDeploymentUrl())
273271
.isEqualTo(
274272
"https://api.ai.intprod-eu12.eu-central-1.aws.ml.hana.ondemand.com/v2/inference/deployments/db1d64d9f06be467");
275-
assertThat(deployment.getDetails().getResources().getBackendDetails()).isNotNull();
276-
assertThat(deployment.getDetails().getScaling().getBackendDetails()).isNotNull();
273+
assertThat(deployment.getDetails().getResources().getBackendDetails()).isEqualTo(Map.of());
274+
assertThat(deployment.getDetails().getResources().getCustomField("backend_details"))
275+
.isEqualTo(Map.of());
276+
assertThat(deployment.getDetails().getScaling().getBackendDetails()).isEqualTo(Map.of());
277+
assertThat(deployment.getDetails().getScaling().getCustomField("backend_details"))
278+
.isEqualTo(Map.of());
277279
assertThat(deployment.getId()).isEqualTo("db1d64d9f06be467");
278280
assertThat(deployment.getLastOperation())
279281
.isEqualTo(AiDeploymentResponseWithDetails.LastOperationEnum.CREATE);

core/src/test/resources/__files/GPT4DeploymentResponse.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@
88
"deploymentUrl": "https://api.ai.intprod-eu12.eu-central-1.aws.ml.hana.ondemand.com/v2/inference/deployments/d19b998f347341aa",
99
"details": {
1010
"resources": {
11-
"backend_details": {
11+
"backendDetails": {
1212
"model": {
1313
"name": "gpt-4-32k",
1414
"version": "latest"
1515
}
1616
}
1717
},
1818
"scaling": {
19-
"backend_details": {}
19+
"backendDetails": {}
2020
}
2121
},
2222
"id": "d19b998f347341aa",

docs/guides/OPENAI_CHAT_COMPLETION.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,10 @@ In addition to the prerequisites above, we assume you have already set up the fo
6565
"ttl": null,
6666
"details": {
6767
"scaling": {
68-
"backendDetails": null,
69-
"backend_details": {}
68+
"backendDetails": {}
7069
},
7170
"resources": {
72-
"backendDetails": null,
73-
"backend_details": {
71+
"backendDetails": {
7472
"model": {
7573
"name": "gpt-35-turbo",
7674
"version": "latest"

docs/guides/ORCHESTRATION_CHAT_COMPLETION.md

Lines changed: 72 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -68,90 +68,84 @@ In addition to the prerequisites above, we assume you have already set up the fo
6868
```
6969

7070
</details>
71-
72-
### Chat completion with Templates
7371

74-
Use a chat completion template to generate a response in German:
72+
### Create a Client
73+
74+
To use the Orchestration service, create a client and a configuration object:
7575

7676
```java
77-
var llmConfig = LLMModuleConfig.create().modelName("gpt-35-turbo").modelParams(Map.of());
77+
var client = new OrchestrationClient();
7878

79-
var inputParams =
80-
Map.of("input", "Reply with 'Orchestration Service is working!' in German");
81-
var template = ChatMessage.create().role("user").content("{{?input}}");
82-
var templatingConfig = TemplatingModuleConfig.create().template(template);
79+
var config = new OrchestrationModuleConfig()
80+
.withLlmConfig(LLMModuleConfig.create().modelName("gpt-35-turbo").modelParams(Map.of()));
81+
```
82+
83+
Please also refer to [our sample code](../../sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/controllers/OrchestrationController.java) for this and all following code examples.
84+
85+
### Chat Completion
8386

84-
var config =
85-
CompletionPostRequest.create()
86-
.orchestrationConfig(
87-
OrchestrationConfig.create()
88-
.moduleConfigurations(
89-
ModuleConfigs.create()
90-
.llmModuleConfig(llmConfig)
91-
.templatingModuleConfig(templatingConfig)))
92-
.inputParams(inputParams);
87+
Use the Orchestration service to generate a response to a user message:
9388

94-
CompletionPostResponse result =
95-
new OrchestrationClient().chatCompletion(config);
89+
```java
90+
var prompt = new OrchestrationPrompt("Hello world! Why is this phrase so famous?");
91+
92+
var result = client.chatCompletion(prompt, config);
9693

9794
String messageResult =
98-
result.getOrchestrationResult().getChoices().get(0).getMessage().getContent();
95+
result.getOrchestrationResult().getChoices().get(0).getMessage().getContent();
9996
```
10097

101-
See [an example in our Spring Boot application](../../sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/controllers/OrchestrationController.java)
98+
In this example, the Orchestration service generates a response to the user message "Hello world! Why is this phrase so famous?".
99+
The LLM response is available as the first choice under the `result.getOrchestrationResult()` object.
102100

103-
### Message history
101+
### Chat completion with Templates
104102

105-
Include a message history to maintain context in the conversation:
103+
Use a prepared template and execute requests with by passing only the input parameters:
106104

107105
```java
108-
var llmConfig = LLMModuleConfig.create().modelName("gpt-35-turbo").modelParams(Map.of());
106+
var template =
107+
ChatMessage.create()
108+
.role("user")
109+
.content("Reply with 'Orchestration Service is working!' in {{?language}}");
110+
var templatingConfig = TemplatingModuleConfig.create().template(template);
111+
var configWithTemplate = config.withTemplateConfig(templatingConfig);
112+
113+
var inputParams = Map.of("language", "German");
114+
var prompt = new OrchestrationPrompt(inputParams);
115+
116+
var result = client.chatCompletion(prompt, configWithTemplate);
117+
```
118+
119+
In this case the template is defined with the placeholder `{{?language}}` which is replaced by the value `German` in the input parameters.
120+
121+
### Message history
109122

110-
List<ChatMessage> messagesHistory =
111-
List.of(
112-
ChatMessage.create().role("user").content("What is the capital of France?"),
113-
ChatMessage.create().role("assistant").content("The capital of France is Paris."));
123+
Include a message history to maintain context in the conversation:
114124

125+
```java
126+
var messagesHistory =
127+
List.of(
128+
ChatMessage.create().role("user").content("What is the capital of France?"),
129+
ChatMessage.create().role("assistant").content("The capital of France is Paris."));
115130
var message =
116-
ChatMessage.create().role("user").content("What is the typical food there?");
117-
var templatingConfig = TemplatingModuleConfig.create().template(message);
118-
119-
var config =
120-
CompletionPostRequest.create()
121-
.orchestrationConfig(
122-
OrchestrationConfig.create()
123-
.moduleConfigurations(
124-
ModuleConfigs.create()
125-
.llmModuleConfig(llmConfig)
126-
.templatingModuleConfig(templatingConfig)))
127-
.messagesHistory(messagesHistory);
128-
129-
CompletionPostResponse result =
130-
new OrchestrationClient().chatCompletion(config);
131+
ChatMessage.create().role("user").content("What is the typical food there?");
131132

132-
String messageResult =
133-
result.getOrchestrationResult().getChoices().get(0).getMessage().getContent();
134-
```
133+
var prompt = new OrchestrationPrompt(message).messageHistory(messagesHistory);
135134

136-
See [an example in our Spring Boot application](../../sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/controllers/OrchestrationController.java)
135+
var result = new OrchestrationClient().chatCompletion(prompt, config);
136+
```
137137

138138
### Chat completion filter
139139

140140
Apply content filtering to the chat completion:
141141

142142
```java
143-
var llmConfig = LLMModuleConfig.create().modelName("gpt-35-turbo").modelParams(Map.of());
144-
145-
var inputParams =
146-
Map.of(
147-
"disclaimer",
148-
"```DISCLAIMER: The area surrounding the apartment is known for prostitutes and gang violence including armed conflicts, gun violence is frequent.");
149-
var template =
150-
ChatMessage.create()
151-
.role("user")
152-
.content(
153-
"Create a rental posting for subletting my apartment in the downtown area. Keep it short. Make sure to add the following disclaimer to the end. Do not change it! {{?disclaimer}}");
154-
var templatingConfig = TemplatingModuleConfig.create().template(template);
143+
var prompt = new OrchestrationPrompt(
144+
"""
145+
Create a rental posting for subletting my apartment in the downtown area. Keep it short. Make sure to add the following disclaimer to the end. Do not change it!
146+
147+
```DISCLAIMER: The area surrounding the apartment is known for prostitutes and gang violence including armed conflicts, gun violence is frequent.
148+
""");
155149

156150
var filterStrict =
157151
FilterConfig.create()
@@ -176,40 +170,21 @@ var filterLoose =
176170
var filteringConfig =
177171
FilteringModuleConfig.create()
178172
// changing the input to filterLoose will allow the message to pass
179-
.input(FilteringConfig.create().filters(filterStrict))
180-
.output(FilteringConfig.create().filters(filterStrict));
181-
182-
var config =
183-
CompletionPostRequest.create()
184-
.orchestrationConfig(
185-
OrchestrationConfig.create()
186-
.moduleConfigurations(
187-
ModuleConfigs.create()
188-
.llmModuleConfig(llmConfig)
189-
.templatingModuleConfig(templatingConfig)
190-
.filteringModuleConfig(filteringConfig)))
191-
.inputParams(inputParams);
173+
.input(InputFilteringConfig.create().filters(filterStrict))
174+
.output(OutputFilteringConfig.create().filters(filterStrict));
192175

193-
// this fails with Bad Request because the strict filter prohibits the input message
194-
CompletionPostResponse result =
195-
new OrchestrationClient().chatCompletion(config);
176+
var configWithFilter = config.withFilteringConfig(filteringConfig);
196177

197-
String messageResult =
198-
result.getOrchestrationResult().getChoices().get(0).getMessage().getContent();
178+
// this fails with Bad Request because the strict filter prohibits the input message
179+
var result =
180+
new OrchestrationClient().chatCompletion(prompt, configWithFilter);
199181
```
200182

201-
See [an example in our Spring Boot application](../../sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/controllers/OrchestrationController.java)
202-
203183
### Data masking
204184

205185
Use the data masking module to anonymize personal information in the input:
206186

207187
```java
208-
var inputParams = Map.of("privateInfo", "Patrick Morgan +49 (970) 333-3833");
209-
var template =
210-
ChatMessage.create().role("user").content("What is the nationality of {{?privateInfo}}");
211-
var templatingConfig = TemplatingModuleConfig.create().template(template);
212-
213188
var maskingProvider =
214189
MaskingProviderConfig.create()
215190
.type(MaskingProviderConfig.TypeEnum.SAP_DATA_PRIVACY_INTEGRATION)
@@ -218,29 +193,26 @@ var maskingProvider =
218193
DPIEntityConfig.create().type(DPIEntities.PHONE),
219194
DPIEntityConfig.create().type(DPIEntities.PERSON));
220195
var maskingConfig = MaskingModuleConfig.create().maskingProviders(maskingProvider);
196+
var configWithMasking = config.withMaskingConfig(maskingConfig);
221197

222-
CompletionPostRequest config =
223-
CompletionPostRequest.create()
224-
.orchestrationConfig(
225-
OrchestrationConfig.create()
226-
.moduleConfigurations(
227-
ModuleConfigs.create()
228-
.llmModuleConfig(LLM_CONFIG)
229-
.templatingModuleConfig(templatingConfig)
230-
.maskingModuleConfig(maskingConfig)))
231-
.inputParams(inputParams);
198+
var systemMessage = ChatMessage.create()
199+
.role("system")
200+
.content("Please evaluate the following user feedback and judge if the sentiment is positive or negative.");
201+
var userMessage = ChatMessage.create()
202+
.role("user")
203+
.content("""
204+
I think the SDK is good, but could use some further enhancements.
205+
My architect Alice and manager Bob pointed out that we need the grounding capabilities, which aren't supported yet.
206+
""");
232207

233-
CompletionPostResponse result =
234-
new OrchestrationClient().chatCompletion(config);
208+
var prompt = new OrchestrationPrompt(systemMessage, userMessage);
235209

236-
String messageResult =
237-
result.getOrchestrationResult().getChoices().get(0).getMessage().getContent();
210+
var result =
211+
new OrchestrationClient().chatCompletion(prompt, configWithMasking);
238212
```
239213

240214
In this example, the input will be masked before the call to the LLM. Note that data cannot be unmasked in the LLM output.
241215

242-
See [an example in our Spring Boot application](../../sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/controllers/OrchestrationController.java)
243-
244216
### Set model parameters
245217

246218
Change your LLM module configuration to add model parameters:

0 commit comments

Comments
 (0)