Skip to content

Commit 49b02c8

Browse files
authored
Merge branch 'main' into doc/cloud-sdk-requirements
2 parents f0dd5cd + c971173 commit 49b02c8

File tree

35 files changed

+965
-314
lines changed

35 files changed

+965
-314
lines changed

.github/workflows/continuous-integration.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ jobs:
2222
with:
2323
fetch-depth: 1
2424
ref: ${{ github.event.pull_request.head.ref }}
25+
repository: ${{ github.event.pull_request.head.repo.full_name }}
2526

2627
- name: "Setup java"
2728
uses: actions/setup-java@v4
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
name: Test SAP Cloud SDK Versions
2+
3+
on:
4+
workflow_dispatch:
5+
6+
env:
7+
MVN_MULTI_THREADED_ARGS: --batch-mode --no-transfer-progress --fail-at-end --show-version --threads 1C
8+
JAVA_VERSION: 17
9+
10+
jobs:
11+
fetch-dependency-versions:
12+
runs-on: ubuntu-latest
13+
outputs:
14+
versions: ${{ steps.fetch-versions.outputs.VERSIONS }}
15+
16+
steps:
17+
- name: Fetch versions from Maven Central
18+
id: fetch-versions
19+
run: |
20+
# Specify the dependency coordinates
21+
GROUP_ID="com.sap.cloud.sdk"
22+
ARTIFACT_ID="sdk-bom"
23+
24+
# Fetch available versions from Maven Central API
25+
response=$(curl -s "https://search.maven.org/solrsearch/select?q=g:%22${GROUP_ID}%22+AND+a:%22${ARTIFACT_ID}%22&rows=15&core=gav&wt=json")
26+
27+
# Extract and filter versions (e.g., to exclude snapshots or specific versions)
28+
versions=$(echo "$response" | jq -r '.response.docs[].v' | grep -v -E 'SNAPSHOT|alpha|beta' | sort -V)
29+
30+
# Convert the versions to a JSON array
31+
json_versions=$(echo "$versions" | jq -R . | jq -s . | tr -d '\n')
32+
33+
echo "JSON Versions: $json_versions"
34+
35+
# Output the versions as a string that can be used in the matrix
36+
echo "VERSIONS=${json_versions}" >> $GITHUB_OUTPUT
37+
38+
setup-environment:
39+
runs-on: ubuntu-latest
40+
outputs:
41+
cache-key: ${{ steps.cache-build.outputs.cache-key }}
42+
steps:
43+
- name: "Checkout repository"
44+
uses: actions/checkout@v4
45+
46+
- name: "Setup java"
47+
uses: actions/setup-java@v4
48+
with:
49+
distribution: "temurin"
50+
java-version: ${{ env.JAVA_VERSION }}
51+
cache: 'maven'
52+
53+
- name: "Cache build"
54+
id: cache-build
55+
uses: actions/cache@v3
56+
with:
57+
path: |
58+
~/.m2/repository
59+
target
60+
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
61+
restore-keys: |
62+
${{ runner.os }}-maven-
63+
64+
- name: "Build SDK"
65+
run: |
66+
MVN_ARGS="${{ env.MVN_MULTI_THREADED_ARGS }} clean install -DskipTests -DskipFormatting"
67+
mvn $MVN_ARGS
68+
69+
test-dependency-versions:
70+
needs: [ fetch-dependency-versions, setup-environment ]
71+
runs-on: ubuntu-latest
72+
strategy:
73+
max-parallel: 1
74+
fail-fast: false
75+
matrix:
76+
version: ${{ fromJson(needs.fetch-dependency-versions.outputs.versions) }}
77+
continue-on-error: true
78+
steps:
79+
- name: "Checkout repository"
80+
uses: actions/checkout@v4
81+
82+
- name: "Setup java"
83+
uses: actions/setup-java@v4
84+
with:
85+
distribution: "temurin"
86+
java-version: ${{ env.JAVA_VERSION }}
87+
cache: 'maven'
88+
89+
- name: "Restore build cache"
90+
uses: actions/cache@v3
91+
with:
92+
path: |
93+
~/.m2/repository
94+
target
95+
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
96+
97+
- name: "Run tests with explicit version"
98+
run: |
99+
MVN_ARGS="${{ env.MVN_MULTI_THREADED_ARGS }} clean package -pl :spring-app -DskipTests=false -DskipFormatting -Dcloud-sdk.version=${{ matrix.version }} -Denforcer.skip=true -Dspotless.skip=true"
100+
mvn $MVN_ARGS
101+
env:
102+
# See "End-to-end test application instructions" on the README.md to update the secret
103+
AICORE_SERVICE_KEY: ${{ secrets.AICORE_SERVICE_KEY }}
104+
105+
- name: "Start Application Locally"
106+
run: |
107+
cd sample-code/spring-app
108+
mvn spring-boot:run &
109+
timeout=15
110+
while ! nc -z localhost 8080; do
111+
sleep 1
112+
timeout=$((timeout - 1))
113+
if [ $timeout -le 0 ]; then
114+
echo "Server did not start within 15 seconds."
115+
exit 1
116+
fi
117+
done
118+
env:
119+
# See "End-to-end test application instructions" on the README.md to update the secret
120+
AICORE_SERVICE_KEY: ${{ secrets.AICORE_SERVICE_KEY }}
121+
122+
- name: "Health Check"
123+
# print response body with headers to stdout. q:body only O:print -:stdout S:headers
124+
run: wget -qO- -S localhost:8080

.github/workflows/e2e-test.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: "End-to-end Tests"
22
on:
33
workflow_dispatch:
44
schedule:
5-
- cron: 0 22 * * *
5+
- cron: 0 2 * * *
66

77
env:
88
MVN_MULTI_THREADED_ARGS: --batch-mode --no-transfer-progress --fail-at-end --show-version --threads 1C

.github/workflows/perform-release.yml

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,6 @@ jobs:
8888
with:
8989
distribution: "sapmachine"
9090
java-version: ${{ env.JAVA_VERSION }}
91-
server-id: ossrh
92-
server-username: MAVEN_CENTRAL_USER # env variable for username in deploy
93-
server-password: MAVEN_CENTRAL_PASSWORD # env variable for token in deploy
9491

9592
- name: "Download Release Asset"
9693
id: download-asset
@@ -113,8 +110,8 @@ jobs:
113110
114111
- name: "Deploy"
115112
run: |
116-
MVN_ARGS="${{ env.MVN_CLI_ARGS }} deploy -Drelease -s settings.xml"
117-
mvn $MVN_ARGS
113+
MVN_ARGS="${{ env.MVN_CLI_ARGS }} -Drelease -s settings.xml"
114+
mvn deploy $MVN_ARGS
118115
env:
119116
MAVEN_GPG_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }}
120117

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ After restarting your application, you should see an "aicore" entry in the `VCAP
171171

172172
- **Name**: `my-aicore`
173173
- **Type**: `HTTP`
174-
- **URL**: `[serviceurls.AI_API_URL]/v2` (append `/v2` to the URL)
174+
- **URL**: `[serviceurls.AI_API_URL]`
175175
- **Proxy Type**: `Internet`
176176
- **Authentication**: `OAuth2ClientCredentials`
177177
- **Client ID**: `[clientid]`

core/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<parent>
55
<groupId>com.sap.ai.sdk</groupId>
66
<artifactId>sdk-parent</artifactId>
7-
<version>0.2.0-SNAPSHOT</version>
7+
<version>0.3.0-SNAPSHOT</version>
88
</parent>
99
<artifactId>core</artifactId>
1010
<name>AI Core client</name>

docs/guides/ORCHESTRATION_CHAT_COMPLETION.md

Lines changed: 25 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ To use the Orchestration service, create a client and a configuration object:
7777
var client = new OrchestrationClient();
7878

7979
var config = new OrchestrationModuleConfig()
80-
.withLlmConfig(LLMModuleConfig.create().modelName("gpt-35-turbo").modelParams(Map.of()));
80+
.withLlmConfig(OrchestrationAiModel.GPT_4O);
8181
```
8282

8383
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.
@@ -91,8 +91,7 @@ var prompt = new OrchestrationPrompt("Hello world! Why is this phrase so famous?
9191

9292
var result = client.chatCompletion(prompt, config);
9393

94-
String messageResult =
95-
result.getOrchestrationResult().getChoices().get(0).getMessage().getContent();
94+
String messageResult = result.getContent();
9695
```
9796

9897
In this example, the Orchestration service generates a response to the user message "Hello world! Why is this phrase so famous?".
@@ -147,33 +146,20 @@ var prompt = new OrchestrationPrompt(
147146
```DISCLAIMER: The area surrounding the apartment is known for prostitutes and gang violence including armed conflicts, gun violence is frequent.
148147
""");
149148

150-
var filterStrict =
151-
FilterConfig.create()
152-
.type(FilterConfig.TypeEnum.AZURE_CONTENT_SAFETY)
153-
.config(
154-
AzureContentSafety.create()
155-
.hate(NUMBER_0)
156-
.selfHarm(NUMBER_0)
157-
.sexual(NUMBER_0)
158-
.violence(NUMBER_0));
159-
160-
var filterLoose =
161-
FilterConfig.create()
162-
.type(FilterConfig.TypeEnum.AZURE_CONTENT_SAFETY)
163-
.config(
164-
AzureContentSafety.create()
165-
.hate(NUMBER_4)
166-
.selfHarm(NUMBER_4)
167-
.sexual(NUMBER_4)
168-
.violence(NUMBER_4));
169-
170-
var filteringConfig =
171-
FilteringModuleConfig.create()
172-
// changing the input to filterLoose will allow the message to pass
173-
.input(InputFilteringConfig.create().filters(filterStrict))
174-
.output(OutputFilteringConfig.create().filters(filterStrict));
175-
176-
var configWithFilter = config.withFilteringConfig(filteringConfig);
149+
var filterStrict = new AzureContentFilter()
150+
.hate(ALLOW_SAFE)
151+
.selfHarm(ALLOW_SAFE)
152+
.sexual(ALLOW_SAFE)
153+
.violence(ALLOW_SAFE);
154+
155+
var filterLoose = new AzureContentFilter()
156+
.hate(ALLOW_SAFE_LOW_MEDIUM)
157+
.selfHarm(ALLOW_SAFE_LOW_MEDIUM)
158+
.sexual(ALLOW_SAFE_LOW_MEDIUM)
159+
.violence(ALLOW_SAFE_LOW_MEDIUM);
160+
161+
// changing the input to filterLoose will allow the message to pass
162+
var configWithFilter = config.withInputFiltering(filterStrict).withOutputFiltering(filterStrict);
177163

178164
// this fails with Bad Request because the strict filter prohibits the input message
179165
var result =
@@ -185,14 +171,8 @@ var result =
185171
Use the data masking module to anonymize personal information in the input:
186172

187173
```java
188-
var maskingProvider =
189-
MaskingProviderConfig.create()
190-
.type(MaskingProviderConfig.TypeEnum.SAP_DATA_PRIVACY_INTEGRATION)
191-
.method(MaskingProviderConfig.MethodEnum.ANONYMIZATION)
192-
.entities(
193-
DPIEntityConfig.create().type(DPIEntities.PHONE),
194-
DPIEntityConfig.create().type(DPIEntities.PERSON));
195-
var maskingConfig = MaskingModuleConfig.create().maskingProviders(maskingProvider);
174+
var maskingConfig =
175+
DpiMasking.anonymization().withEntities(DPIEntities.PHONE, DPIEntities.PERSON);
196176
var configWithMasking = config.withMaskingConfig(maskingConfig);
197177

198178
var systemMessage = ChatMessage.create()
@@ -211,20 +191,20 @@ var result =
211191
new OrchestrationClient().chatCompletion(prompt, configWithMasking);
212192
```
213193

214-
In this example, the input will be masked before the call to the LLM. Note that data cannot be unmasked in the LLM output.
194+
In this example, the input will be masked before the call to the LLM and will remain masked in the output.
215195

216196
### Set model parameters
217197

218-
Change your LLM module configuration to add model parameters:
198+
Change your LLM configuration to add model parameters:
219199

220200
```java
221-
var llmConfig =
222-
LLMModuleConfig.create()
223-
.modelName("gpt-35-turbo")
224-
.modelParams(
201+
OrchestrationAiModel customGPT4O =
202+
OrchestrationAiModel.GPT_4O
203+
.withParams(
225204
Map.of(
226205
"max_tokens", 50,
227206
"temperature", 0.1,
228207
"frequency_penalty", 0,
229-
"presence_penalty", 0));
208+
"presence_penalty", 0))
209+
.withVersion("2024-05-13");
230210
```

foundation-models/openai/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<parent>
55
<groupId>com.sap.ai.sdk</groupId>
66
<artifactId>sdk-parent</artifactId>
7-
<version>0.2.0-SNAPSHOT</version>
7+
<version>0.3.0-SNAPSHOT</version>
88
<relativePath>../../pom.xml</relativePath>
99
</parent>
1010
<groupId>com.sap.ai.sdk.foundationmodels</groupId>

foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/model/OpenAiChatCompletionOutput.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,7 @@ public class OpenAiChatCompletionOutput extends OpenAiCompletionOutput
4040
*/
4141
@Nonnull
4242
public String getContent() throws OpenAiClientException {
43-
if (getChoices().isEmpty()) {
44-
return "";
45-
}
43+
// We expect choices to be defined and never empty.
4644
if ("content_filter".equals(getChoices().get(0).getFinishReason())) {
4745
throw new OpenAiClientException("Content filter filtered the output.");
4846
}

foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/model/OpenAiChatMessage.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ class OpenAiChatAssistantMessage implements OpenAiChatMessage {
250250
/** The tool calls generated by the model, such as function calls. */
251251
@JsonProperty("tool_calls")
252252
@Getter(onMethod_ = @Nullable)
253-
private List<OpenAiChatToolCall> tool_calls;
253+
private List<OpenAiChatToolCall> toolCalls;
254254

255255
// TODO: add context
256256
// https://github.com/Azure/azure-rest-api-specs/blob/07d286359f828bbc7901e86288a5d62b48ae2052/specification/cognitiveservices/data-plane/AzureOpenAI/inference/stable/2024-02-01/inference.json#L1599
@@ -264,12 +264,11 @@ void addDelta(@Nonnull final OpenAiChatAssistantMessage delta) {
264264
content += delta.getContent();
265265
}
266266

267-
if (delta.getTool_calls() != null) {
268-
if (tool_calls == null) {
269-
tool_calls = new ArrayList<>();
267+
if (delta.getToolCalls() != null) {
268+
if (toolCalls == null) {
269+
toolCalls = new ArrayList<>();
270270
}
271-
// TODO: camel case
272-
tool_calls.addAll(delta.getTool_calls());
271+
toolCalls.addAll(delta.getToolCalls());
273272
}
274273
}
275274
}

0 commit comments

Comments
 (0)