Skip to content

Commit 29bf0b4

Browse files
authored
Merge branch 'main' into chore/skip-generation-bydefault
2 parents 16fe7f0 + 868c635 commit 29bf0b4

File tree

31 files changed

+771
-390
lines changed

31 files changed

+771
-390
lines changed

.pipeline/checkstyle-suppressions.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66

77
<suppressions>
88
<!-- Suppress generated clients -->
9-
<suppress files="/core/client/" checks=".*"/>
10-
<suppress files="/orchestration/client/" checks=".*"/>
9+
<suppress files="[/\\]core[/\\]client[/\\]" checks=".*"/>
10+
<suppress files="[/\\]orchestration[/\\]client[/\\]" checks=".*"/>
1111
<!-- Suppress TODOs -->
1212
<suppress files="OpenAiChatCompletionParameters.java" checks="TodoComment" lines="95" />
1313
<suppress files="OpenAiChatMessage.java" checks="TodoComment" lines="255,271" />

README.md

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
[![REUSE status](https://api.reuse.software/badge/git.fsfe.org/reuse/api)](https://api.reuse.software/info/git.fsfe.org/reuse/api)
44
[![Fosstars security rating](https://github.com/SAP/cloud-sdk-java/blob/fosstars-report/fosstars_badge.svg)](https://github.com/SAP/cloud-sdk-java/blob/fosstars-report/fosstars_report.md)
55

6-
# SAP Cloud SDK for AI (for Java)
6+
# <img src="https://sap.github.io/cloud-sdk/img/logo.svg" alt="SAP Cloud SDK" width="30"/> SAP Cloud SDK for AI (for Java)
77

88
> ⚠️ **This is a pre-alpha version of the AI SDK for Java. The APIs are subject to change.** ⚠️
99
@@ -12,7 +12,7 @@
1212
- [Introduction](#introduction)
1313
- [General Requirements](#general-requirements)
1414
- [Connecting to SAP AI Core](#connecting-to-sap-ai-core)
15-
- [Option 1: Set Credentials as Environment Variable](#option-1-set-credentials-as-environment-variable)
15+
- [Option 1: Set Credentials as Environment Variable](#option-1-set-ai-core-credentials)
1616
- [Option 2: Regular Service Binding in SAP BTP Cloud Foundry](#option-2-regular-service-binding-in-sap-btp-cloud-foundry)
1717
- [Option 3: Define and Use a Destination](#option-3-define-and-use-a-destination)
1818
- [Getting Started](#getting-started)
@@ -64,7 +64,7 @@ There are multiple ways to provide these credentials:
6464

6565
| Option | Description |
6666
|--------|----------------------------------------------------------------------------------------------------------|
67-
| **1** | Set an environment variable explicitly: `AICORE_SERVICE_KEY` |
67+
| **1** | Create an `.env` file containing an `AICORE_SERVICE_KEY={...}` |
6868
| **2** | Regular service binding in SAP BTP Cloud Foundry (results in `VCAP_SERVICES` environment variable entry) |
6969
| **3** | Define and use a _Destination_ in the SAP BTP Destination Service |
7070

@@ -75,7 +75,7 @@ Additional methods (not recommended for production):
7575
- Leverage a "user-provided" service binding
7676
- Define and use a custom `ServiceBinding` or `ServiceBindingAccessor` in your application
7777

78-
### Option 1: Set Credentials as Environment Variable
78+
### Option 1: Set AI Core Credentials
7979

8080
<details>
8181
<summary>Click to view detailed steps</summary>
@@ -87,16 +87,33 @@ Additional methods (not recommended for production):
8787
- Navigate to **Instances and Subscriptions** -> **Instances** -> **AI Core**
8888
- Click **View Credentials** and copy the JSON content
8989

90-
**2. Set Environment Variable:**
90+
**2. Create `.env` file:**
91+
92+
- Create an `.env` file in the root directory of your application
93+
- Add an entry `AICORE_SERVICE_KEY='<content-of-service-key>'`
94+
95+
<details>
96+
<summary>Set an environment variable instead of .env</summary>
97+
98+
**2. Set an Environment Variable: (alternative)**
9199

92100
- In your IDE or terminal, set the environment variable `AICORE_SERVICE_KEY` with the copied JSON content
93101

94-
Example:
102+
ℹ️ The environment variable has priority over the `.env` file.
103+
104+
Example Linux/MacOS:
95105

96106
```shell
97107
export AICORE_SERVICE_KEY='{ "clientid": "...", "clientsecret": "...", "url": "...", "serviceurls": { "AI_API_URL": "..." } }'
98108
```
99109

110+
Example Windows:
111+
112+
```shell
113+
$env:AICORE_SERVICE_KEY='{ "clientid": "...", "clientsecret": "...", "url": "...", "serviceurls": { "AI_API_URL": "..." } }'
114+
```
115+
116+
</details>
100117
</details>
101118

102119
### Option 2: Regular Service Binding in SAP BTP Cloud Foundry
@@ -180,7 +197,7 @@ Before you begin, ensure you have:
180197
- Met the OpenAI Chat Completion module specific requirements
181198
- Refer to [Prerequisites for OpenAI Chat Completion](docs/guides/OPENAI_CHAT_COMPLETION.md#prerequisites)
182199
- Set up the AI Core credentials
183-
using [(1) Environment Variable](#option-1-set-credentials-as-environment-variable)
200+
using [(1) Environment variable or env-file](#option-1-set-ai-core-credentials)
184201
or [(2) Regular Service Binding](#option-2-regular-service-binding-in-sap-btp-cloud-foundry).
185202
- Deployed the OpenAI GPT-3.5 Turbo model in SAP AI Core.
186203
- Refer to [Deploying the OpenAI GPT-3.5 Turbo Model](docs/guides/OPENAI_CHAT_COMPLETION.md#usage)

core/pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,10 @@
9898
<groupId>io.vavr</groupId>
9999
<artifactId>vavr</artifactId>
100100
</dependency>
101+
<dependency>
102+
<groupId>io.github.cdimascio</groupId>
103+
<artifactId>dotenv-java</artifactId>
104+
</dependency>
101105
<!-- scope "runtime" -->
102106
<dependency>
103107
<groupId>com.sap.cloud.sdk.cloudplatform</groupId>

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import com.sap.cloud.sdk.cloudplatform.connectivity.exception.DestinationAccessException;
1616
import com.sap.cloud.sdk.cloudplatform.connectivity.exception.DestinationNotFoundException;
1717
import com.sap.cloud.sdk.services.openapi.apiclient.ApiClient;
18+
import io.github.cdimascio.dotenv.Dotenv;
1819
import java.util.NoSuchElementException;
1920
import java.util.function.BiFunction;
2021
import java.util.function.Function;
@@ -104,7 +105,7 @@ protected void destinationSetHeaders(@Nonnull final DefaultHttpDestination.Build
104105
*/
105106
@Nonnull
106107
public AiCoreService withDestination(@Nonnull final Destination destination) {
107-
baseDestinationHandler = (service) -> destination;
108+
baseDestinationHandler = service -> destination;
108109
return this;
109110
}
110111

@@ -160,7 +161,7 @@ public AiCoreDeployment forDeploymentByScenario(@Nonnull final String scenarioId
160161
@Nonnull
161162
protected Destination getBaseDestination()
162163
throws DestinationAccessException, DestinationNotFoundException {
163-
val serviceKey = System.getenv("AICORE_SERVICE_KEY");
164+
val serviceKey = Dotenv.configure().ignoreIfMissing().load().get("AICORE_SERVICE_KEY");
164165
return DestinationResolver.getDestination(serviceKey);
165166
}
166167

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,13 @@
2222
import javax.annotation.Nullable;
2323
import lombok.AccessLevel;
2424
import lombok.Getter;
25+
import lombok.NoArgsConstructor;
2526
import lombok.extern.slf4j.Slf4j;
2627
import lombok.val;
2728

2829
/** Utility class to resolve the destination pointing to the AI Core service. */
2930
@Slf4j
31+
@NoArgsConstructor(access = AccessLevel.PRIVATE) // utility class should not be instantiated
3032
class DestinationResolver {
3133
static final String AI_CLIENT_TYPE_KEY = "URL.headers.AI-Client-Type";
3234
static final String AI_CLIENT_TYPE_VALUE = "AI SDK Java";

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
import org.junit.jupiter.api.AfterEach;
2626
import org.junit.jupiter.api.Test;
2727

28-
public class AiCoreServiceTest {
28+
class AiCoreServiceTest {
2929

3030
// setup
3131
private static final Map<String, Object> URLS = Map.of("AI_API_URL", "https://srv");

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
* Test that queries are on the right URL, with the right headers. Also check that the received
2020
* response is parsed correctly in the generated client.
2121
*/
22-
public class ArtifactUnitTest extends WireMockTestServer {
22+
class ArtifactUnitTest extends WireMockTestServer {
2323
@Test
2424
void getArtifacts() {
2525
wireMockServer.stubFor(

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
* Test that queries are on the right URL, with the right headers. Also check that the received
2222
* response is parsed correctly in the generated client.
2323
*/
24-
public class ConfigurationUnitTest extends WireMockTestServer {
24+
class ConfigurationUnitTest extends WireMockTestServer {
2525
@Test
2626
void getConfigurations() {
2727
wireMockServer.stubFor(

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

Lines changed: 40 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
* Test that queries are on the right URL, with the right headers. Also check that the received
3333
* response is parsed correctly in the generated client.
3434
*/
35-
public class DeploymentUnitTest extends WireMockTestServer {
35+
class DeploymentUnitTest extends WireMockTestServer {
3636
@Test
3737
void getDeployments() {
3838
wireMockServer.stubFor(
@@ -48,32 +48,43 @@ void getDeployments() {
4848
"count": 1,
4949
"resources": [
5050
{
51+
"id": "d889e3a61050c085",
52+
"deploymentUrl": "https://api.ai.intprod-eu12.eu-central-1.aws.ml.hana.ondemand.com/v2/inference/deployments/d889e3a61050c085",
5153
"configurationId": "7652a231-ba9b-4fcc-b473-2c355cb21b61",
5254
"configurationName": "gpt-4-32k",
53-
"createdAt": "2024-04-17T15:19:53Z",
54-
"deploymentUrl": "https://api.ai.intprod-eu12.eu-central-1.aws.ml.hana.ondemand.com/v2/inference/deployments/d19b998f347341aa",
55+
"executableId": null,
56+
"scenarioId": "foundation-models",
57+
"status": "RUNNING",
58+
"statusMessage": null,
59+
"targetStatus": "RUNNING",
60+
"lastOperation": "CREATE",
61+
"latestRunningConfigurationId": "7652a231-ba9b-4fcc-b473-2c355cb21b61",
62+
"ttl": null,
5563
"details": {
64+
"scaling": {
65+
"backendDetails": {},
66+
"backend_details": {}
67+
},
5668
"resources": {
69+
"backendDetails": {
70+
"model": {
71+
"name": "gpt-4-32k",
72+
"version": "latest"
73+
}
74+
},
5775
"backend_details": {
5876
"model": {
5977
"name": "gpt-4-32k",
6078
"version": "latest"
6179
}
6280
}
63-
},
64-
"scaling": {
65-
"backend_details": {}
6681
}
6782
},
68-
"id": "d19b998f347341aa",
69-
"lastOperation": "CREATE",
70-
"latestRunningConfigurationId": "7652a231-ba9b-4fcc-b473-2c355cb21b61",
71-
"modifiedAt": "2024-05-07T13:05:45Z",
72-
"scenarioId": "foundation-models",
73-
"startTime": "2024-04-17T15:21:15Z",
74-
"status": "RUNNING",
75-
"submissionTime": "2024-04-17T15:20:11Z",
76-
"targetStatus": "RUNNING"
83+
"createdAt": "2024-09-20T11:31:24Z",
84+
"modifiedAt": "2024-10-30T13:36:38Z",
85+
"submissionTime": "2024-09-20T11:31:38Z",
86+
"startTime": "2024-09-20T11:32:42Z",
87+
"completionTime": null
7788
}
7889
]
7990
}
@@ -89,24 +100,22 @@ void getDeployments() {
89100

90101
assertThat(deployment.getConfigurationId()).isEqualTo("7652a231-ba9b-4fcc-b473-2c355cb21b61");
91102
assertThat(deployment.getConfigurationName()).isEqualTo("gpt-4-32k");
92-
assertThat(deployment.getCreatedAt()).isEqualTo("2024-04-17T15:19:53Z");
103+
assertThat(deployment.getCreatedAt()).isEqualTo("2024-09-20T11:31:24Z");
93104
assertThat(deployment.getDeploymentUrl())
94105
.isEqualTo(
95-
"https://api.ai.intprod-eu12.eu-central-1.aws.ml.hana.ondemand.com/v2/inference/deployments/d19b998f347341aa");
106+
"https://api.ai.intprod-eu12.eu-central-1.aws.ml.hana.ondemand.com/v2/inference/deployments/d889e3a61050c085");
96107
// Response contains key "backend_details" while spec (mistakenly) defines key "backendDetails".
97108
val expected = Map.of("model", Map.of("name", "gpt-4-32k", "version", "latest"));
98-
assertThat(deployment.getDetails().getResources().getCustomField("backend_details"))
99-
.isEqualTo(expected);
100-
assertThat(deployment.getDetails().getScaling().getCustomFieldNames())
101-
.contains("backend_details");
102-
assertThat(deployment.getId()).isEqualTo("d19b998f347341aa");
109+
assertThat(deployment.getDetails().getResources().getBackendDetails()).isEqualTo(expected);
110+
assertThat(deployment.getDetails().getScaling().getBackendDetails()).isEqualTo(Map.of());
111+
assertThat(deployment.getId()).isEqualTo("d889e3a61050c085");
103112
assertThat(deployment.getLastOperation()).isEqualTo(AiDeployment.LastOperationEnum.CREATE);
104113
assertThat(deployment.getLatestRunningConfigurationId())
105114
.isEqualTo("7652a231-ba9b-4fcc-b473-2c355cb21b61");
106-
assertThat(deployment.getModifiedAt()).isEqualTo("2024-05-07T13:05:45Z");
107-
assertThat(deployment.getStartTime()).isEqualTo("2024-04-17T15:21:15Z");
115+
assertThat(deployment.getModifiedAt()).isEqualTo("2024-10-30T13:36:38Z");
116+
assertThat(deployment.getStartTime()).isEqualTo("2024-09-20T11:32:42Z");
108117
assertThat(deployment.getStatus()).isEqualTo(AiDeploymentStatus.RUNNING);
109-
assertThat(deployment.getSubmissionTime()).isEqualTo("2024-04-17T15:20:11Z");
118+
assertThat(deployment.getSubmissionTime()).isEqualTo("2024-09-20T11:31:38Z");
110119
assertThat(deployment.getTargetStatus()).isEqualTo(AiDeployment.TargetStatusEnum.RUNNING);
111120
}
112121

@@ -234,10 +243,12 @@ void getDeploymentById() {
234243
"deploymentUrl": "https://api.ai.intprod-eu12.eu-central-1.aws.ml.hana.ondemand.com/v2/inference/deployments/db1d64d9f06be467",
235244
"details": {
236245
"resources": {
237-
"backend_details": {}
246+
"backend_details": {},
247+
"backendDetails": {}
238248
},
239249
"scaling": {
240-
"backend_details": {}
250+
"backend_details": {},
251+
"backendDetails": {}
241252
}
242253
},
243254
"id": "db1d64d9f06be467",
@@ -261,11 +272,8 @@ void getDeploymentById() {
261272
assertThat(deployment.getDeploymentUrl())
262273
.isEqualTo(
263274
"https://api.ai.intprod-eu12.eu-central-1.aws.ml.hana.ondemand.com/v2/inference/deployments/db1d64d9f06be467");
264-
// Response contains key "backend_details" while spec (mistakenly) defines key "backendDetails".
265-
assertThat(deployment.getDetails().getResources().getCustomFieldNames())
266-
.contains("backend_details");
267-
assertThat(deployment.getDetails().getScaling().getCustomFieldNames())
268-
.contains("backend_details");
275+
assertThat(deployment.getDetails().getResources().getBackendDetails()).isNotNull();
276+
assertThat(deployment.getDetails().getScaling().getBackendDetails()).isNotNull();
269277
assertThat(deployment.getId()).isEqualTo("db1d64d9f06be467");
270278
assertThat(deployment.getLastOperation())
271279
.isEqualTo(AiDeploymentResponseWithDetails.LastOperationEnum.CREATE);

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
* Test that queries are on the right URL, with the right headers. Also check that the received
3232
* response is parsed correctly in the generated client.
3333
*/
34-
public class ExecutionUnitTest extends WireMockTestServer {
34+
class ExecutionUnitTest extends WireMockTestServer {
3535
@Test
3636
void getExecutions() {
3737
wireMockServer.stubFor(
@@ -100,7 +100,7 @@ void getExecutions() {
100100
val aiArtifact = execution.getOutputArtifacts().get(0);
101101

102102
assertThat(aiArtifact.getCreatedAt()).isEqualTo("2023-08-05T14:10:05Z");
103-
assertThat(aiArtifact.getDescription()).isEqualTo("");
103+
assertThat(aiArtifact.getDescription()).isEmpty();
104104
assertThat(aiArtifact.getExecutionId()).isEqualTo("eab289226fe981da");
105105
assertThat(aiArtifact.getId()).isEqualTo("be0d728f-1cb2-4ff4-97ad-45c54ac592f6");
106106
assertThat(aiArtifact.getKind()).isEqualTo(AiArtifact.KindEnum.MODEL);
@@ -211,7 +211,7 @@ void getExecutionById() {
211211
val aiArtifact = execution.getOutputArtifacts().get(0);
212212

213213
assertThat(aiArtifact.getCreatedAt()).isEqualTo("2024-09-09T19:10:48Z");
214-
assertThat(aiArtifact.getDescription()).isEqualTo("");
214+
assertThat(aiArtifact.getDescription()).isEmpty();
215215
assertThat(aiArtifact.getExecutionId()).isEqualTo("e529e8bd58740bc9");
216216
assertThat(aiArtifact.getId()).isEqualTo("c4792df8-da67-44fe-ad99-b5ea74bbb248");
217217
assertThat(aiArtifact.getKind()).isEqualTo(AiArtifact.KindEnum.MODEL);

0 commit comments

Comments
 (0)