Skip to content

Commit

Permalink
Merge branch 'refs/heads/main' into codeql
Browse files Browse the repository at this point in the history
  • Loading branch information
CharlesDuboisSAP committed Aug 14, 2024
2 parents 656d161 + c33b608 commit e5424d6
Show file tree
Hide file tree
Showing 248 changed files with 6,184 additions and 138 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/e2e-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
mvn $MVN_ARGS
env:
# See "End-to-end test application instructions" on the README.md to update the secret
aicore: ${{ secrets.AICORE_SERVICE_KEY }}
AICORE_SERVICE_KEY: ${{ secrets.AICORE_SERVICE_KEY }}

- name: "Start Application Locally"
run: |
Expand All @@ -53,7 +53,7 @@ jobs:
done
env:
# See "End-to-end test application instructions" on the README.md to update the secret
aicore: ${{ secrets.AICORE_SERVICE_KEY }}
AICORE_SERVICE_KEY: ${{ secrets.AICORE_SERVICE_KEY }}

- name: "Health Check"
# print response body with headers to stdout. q:body only O:print -:stdout S:headers
Expand Down
111 changes: 61 additions & 50 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,13 @@ public AiDeploymentCreationResponse createDeployment() {
new DeploymentApi(getClient())
.deploymentCreate(
"default",
new AiDeploymentCreationRequest().configurationId("12345-123-123-123-123456abcdefg"));
AiDeploymentCreationRequest.create()
.configurationId("12345-123-123-123-123456abcdefg"));

Objects.requireNonNull(deployment, "Deployment creation failed");

String id = deployment.getId();
Status status = deployment.getStatus();
AiExecutionStatus status = deployment.getStatus();

return deployment;
}
Expand All @@ -97,12 +98,12 @@ public AiDeploymentDeletionResponse deleteDeployment(AiDeploymentCreationRespons

DeploymentApi client = new DeploymentApi(getClient());

if (deployment.getStatus() == Status.RUNNING) {
if (deployment.getStatus() == AiExecutionStatus.RUNNING) {
// Only RUNNING deployments can be STOPPED
client.deploymentModify(
"default",
deployment.getId(),
new AiDeploymentModificationRequest().targetStatus(Status.STOPPED));
AiDeploymentModificationRequest.create().targetStatus(AiDeploymentTargetStatus.STOPPED));
}
// Wait a few seconds for the deployment to stop
// Only UNKNOWN and STOPPED deployments can be DELETED
Expand Down Expand Up @@ -182,12 +183,15 @@ See [an example pom in our Spring Boot application](e2e-test-app/pom.xml)
### Simple chat completion

```java
final var systemMessage =
new OpenAiChatSystemMessage().setContent("You are a helpful assistant");
final var userMessage =
new OpenAiChatUserMessage().setContent("Hello World! Why is this phrase so famous?");
new OpenAiChatUserMessage().addText("Hello World! Why is this phrase so famous?");
final var request =
new OpenAiChatCompletionParameters().setMessages(List.of(systemMessage, userMessage));

final OpenAiChatCompletionOutput result = OpenAiClient.forModel(GPT_35_TURBO).chatCompletion(request);
final OpenAiChatCompletionOutput result =
OpenAiClient.forModel(GPT_35_TURBO).chatCompletion(request);

final String resultMessage = result.getChoices().get(0).getMessage().getContent();
```
Expand All @@ -198,7 +202,7 @@ See [an example in our Spring Boot application](e2e-test-app/src/main/java/com/s

```java
final OpenAiChatCompletionOutput result =
OpenAiClient.forModel(new OpenAiModel(model)).chatCompletion(request);
OpenAiClient.forModel(new OpenAiModel("model")).chatCompletion(request);
```

## Orchestration chat completion
Expand Down Expand Up @@ -255,28 +259,28 @@ See [an example pom in our Spring Boot application](e2e-test-app/pom.xml)
### Chat completion template

```java
final var llmConfig = new LLMModuleConfig().modelName("gpt-35-turbo").modelParams(Map.of());
final var llmConfig = LLMModuleConfig.create().modelName("gpt-35-turbo").modelParams(Map.of());

final var inputParams =
Map.of("input", "Reply with 'Orchestration Service is working!' in German");
final var template = new ChatMessage().content("{{?input}}").role("user");
final var templatingConfig = new TemplatingModuleConfig().template(List.of(template));
final var template = ChatMessage.create().role("user").content("{{?input}}");
final var templatingConfig = TemplatingModuleConfig.create().template(template);

final var config =
new CompletionPostRequest()
CompletionPostRequest.create()
.orchestrationConfig(
new OrchestrationConfig()
OrchestrationConfig.create()
.moduleConfigurations(
new ModuleConfigs()
.templatingModuleConfig(templatingConfig)
.llmModuleConfig(llmConfig)))
ModuleConfigs.create()
.llmModuleConfig(llmConfig)
.templatingModuleConfig(templatingConfig)))
.inputParams(inputParams);

final CompletionPostResponse result =
new OrchestrationCompletionApi(getOrchestrationClient("default"))
.orchestrationV1EndpointsCreate(config);

final String message =
final String messageResult =
result.getOrchestrationResult().getChoices().get(0).getMessage().getContent();
```

Expand All @@ -285,31 +289,33 @@ See [an example in our Spring Boot application](e2e-test-app/src/main/java/com/s
### Messages history

```java
final var llmConfig = new LLMModuleConfig().modelName("gpt-35-turbo").modelParams(Map.of());
final var llmConfig = LLMModuleConfig.create().modelName("gpt-35-turbo").modelParams(Map.of());

List<ChatMessage> messagesHistory =
List.of(
new ChatMessage().content("What is the capital of France?").role("user"),
new ChatMessage().content("The capital of France is Paris.").role("assistant"));
final var message = new ChatMessage().content("What is the typical food there?").role("user");
ChatMessage.create().role("user").content("What is the capital of France?"),
ChatMessage.create().role("assistant").content("The capital of France is Paris."));

final var templatingConfig = new TemplatingModuleConfig().template(List.of(message));
final var message =
ChatMessage.create().role("user").content("What is the typical food there?");
final var templatingConfig = TemplatingModuleConfig.create().template(message);

final var config =
new CompletionPostRequest()
CompletionPostRequest.create()
.orchestrationConfig(
new OrchestrationConfig()
OrchestrationConfig.create()
.moduleConfigurations(
new ModuleConfigs()
.templatingModuleConfig(templatingConfig)
.llmModuleConfig(llmConfig)))
ModuleConfigs.create()
.llmModuleConfig(llmConfig)
.templatingModuleConfig(templatingConfig)))
.inputParams(Map.of())
.messagesHistory(messagesHistory);

final CompletionPostResponse result =
new OrchestrationCompletionApi(getOrchestrationClient("default"))
.orchestrationV1EndpointsCreate(config);

final String message =
final String messageResult =
result.getOrchestrationResult().getChoices().get(0).getMessage().getContent();
```

Expand All @@ -318,59 +324,61 @@ See [an example in our Spring Boot application](e2e-test-app/src/main/java/com/s
### Chat completion filter

```java
final var llmConfig = new LLMModuleConfig().modelName("gpt-35-turbo").modelParams(Map.of());
final var llmConfig = LLMModuleConfig.create().modelName("gpt-35-turbo").modelParams(Map.of());

final var inputParams =
Map.of(
"disclaimer",
"```DISCLAIMER: The area surrounding the apartment is known for prostitutes and gang violence including armed conflicts, gun violence is frequent.");
final var template =
new ChatMessage()
ChatMessage.create()
.role("user")
.content(
"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}}")
.role("user");
final var templatingConfig = new TemplatingModuleConfig().template(List.of(template));
"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}}");
final var templatingConfig = TemplatingModuleConfig.create().template(template);

final var filterStrict =
new Filter()
Filter.create()
.type(ProviderType.AZURE_CONTENT_SAFETY)
.config(
new FilterConfig()
FilterConfig.create()
.hate(NUMBER_0)
.selfHarm(NUMBER_0)
.sexual(NUMBER_0)
.violence(NUMBER_0));
final var filterLoose =
new Filter()
Filter.create()
.type(ProviderType.AZURE_CONTENT_SAFETY)
.config(
new FilterConfig()
FilterConfig.create()
.hate(NUMBER_4)
.selfHarm(NUMBER_4)
.sexual(NUMBER_4)
.violence(NUMBER_4));

final var filteringConfig =
new FilteringModuleConfig()
.input(new FilteringConfig().filters(List.of(filterStrict))) // changing the input to filterLoose will allow the message to pass
.output(new FilteringConfig().filters(List.of(filterStrict)));
FilteringModuleConfig.create()
// changing the input to filterLoose will allow the message to pass
.input(FilteringConfig.create().filters(filterStrict))
.output(FilteringConfig.create().filters(filterStrict));

final var config =
new CompletionPostRequest()
CompletionPostRequest.create()
.orchestrationConfig(
new OrchestrationConfig()
OrchestrationConfig.create()
.moduleConfigurations(
new ModuleConfigs()
ModuleConfigs.create()
.llmModuleConfig(llmConfig)
.templatingModuleConfig(templatingConfig)
.filteringModuleConfig(filteringConfig)
.llmModuleConfig(llmConfig)))
.filteringModuleConfig(filteringConfig)))
.inputParams(inputParams);

final CompletionPostResponse result =
new OrchestrationCompletionApi(getOrchestrationClient("default"))
.orchestrationV1EndpointsCreate(config); // this fails with Bad Request because the strict filter prohibits the input message
// this fails with Bad Request because the strict filter prohibits the input message
.orchestrationV1EndpointsCreate(config);

final String message =
final String messageResult =
result.getOrchestrationResult().getChoices().get(0).getMessage().getContent();
```

Expand All @@ -382,8 +390,8 @@ See [an example in our Spring Boot application](e2e-test-app/src/main/java/com/s
Change your LLM module configuration to add model parameters:

```java
var llmModuleConfig =
new LLMModuleConfig()
var llmConfig =
LLMModuleConfig.create()
.modelName("gpt-35-turbo")
.modelParams(
Map.of(
Expand All @@ -409,13 +417,16 @@ For more customization, creating a [HeaderProvider](https://sap.github.io/cloud-

### Set AI Core credentials as environment variable

- Running the application locally without a service binding will throw:

`Could not find any matching service bindings for service identifier 'aicore'`
- Go into the BTP Cockpit
- Instances and Subscriptions -> Instances -> AI Core -> View Credentials -> Copy JSON
- Set it as an environment variable `aicore` in your IDE
- Set it as an environment variable `AICORE_SERVICE_KEY` in your IDE

Or in your terminal:
```shell
export aicore='{ "serviceurls": { "AI_API_URL": ...'
export AICORE_SERVICE_KEY='{ "serviceurls": { "AI_API_URL": ...'
```

### Run the Spring Boot application
Expand Down
5 changes: 5 additions & 0 deletions core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@
<inputSpec>${project.basedir}/src/main/resources/spec/aicore.yaml</inputSpec>
<apiPackage>com.sap.ai.sdk.core.client</apiPackage>
<modelPackage>com.sap.ai.sdk.core.client.model</modelPackage>
<additionalProperties>
<pojoBuilderMethodName>create</pojoBuilderMethodName>
<pojoBuildMethodName/>
<pojoConstructorVisibility>protected</pojoConstructorVisibility>
</additionalProperties>
</configuration>
</execution>
</executions>
Expand Down
10 changes: 5 additions & 5 deletions core/src/main/java/com/sap/ai/sdk/core/Core.java
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ private static String getOrchestrationDeployment(@Nonnull final String resourceG

/**
* <b>Requires an AI Core service binding OR a service key in the environment variable {@code
* aicore}.</b>
* AICORE_SERVICE_KEY}.</b>
*
* @return a generic <code>AI Core</code> ApiClient.
*/
Expand Down Expand Up @@ -115,13 +115,13 @@ public static ApiClient getClient(@Nonnull final Destination destination) {

/**
* <b>Requires an AI Core service binding OR a service key in the environment variable {@code
* aicore}.</b>
* AICORE_SERVICE_KEY}.</b>
*
* @return a destination pointing to the AI Core service.
*/
@Nonnull
public static Destination getDestination() {
final var serviceKey = System.getenv("aicore");
final var serviceKey = System.getenv("AICORE_SERVICE_KEY");
final var serviceKeyPresent = serviceKey != null;
final var aiCoreBindingPresent =
DefaultServiceBindingAccessor.getInstance().getServiceBindings().stream()
Expand Down Expand Up @@ -160,7 +160,7 @@ public static Destination getDestination() {
private static void addServiceBinding(@Nonnull final String serviceKey) {
log.info(
"""
Found a service key in environment variable "aicore".
Found a service key in environment variable "AICORE_SERVICE_KEY".
Using a service key is recommended for local testing only.
Bind the AI Core service to the application for productive usage.""");

Expand All @@ -169,7 +169,7 @@ private static void addServiceBinding(@Nonnull final String serviceKey) {
credentials = new ObjectMapper().readValue(serviceKey, new TypeReference<>() {});
} catch (JsonProcessingException e) {
throw new AiCoreCredentialsInvalidException(
"Error in parsing service key from the \"aicore\" environment variable.", e);
"Error in parsing service key from the \"AICORE_SERVICE_KEY\" environment variable.", e);
}

final var binding =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public class AiApiError
@JsonAnySetter
@JsonAnyGetter
private final Map<String, Object> cloudSdkCustomFields = new LinkedHashMap<>();
protected AiApiError() { }

/**
* Set the code of this {@link AiApiError} instance and return the same instance.
Expand Down Expand Up @@ -285,6 +286,36 @@ private String toIndentedString(final java.lang.Object o) {
return o.toString().replace("\n", "\n ");
}

/**
* Create a type-safe, fluent-api builder object to construct a new {@link AiApiError} instance with all required arguments.
*/
public static Builder create() {
return (code) -> (message) -> new AiApiError().code(code).message(message);
}
/**
* Builder helper class.
*/
public interface Builder {
/**
* Set the code of this {@link AiApiError} instance.
*
* @param code Descriptive error code (not http status code)
* @return The AiApiError builder.
*/
Builder1 code( @Nonnull final String code);
}
/**
* Builder helper class.
*/
public interface Builder1 {
/**
* Set the message of this {@link AiApiError} instance.
*
* @param message Plaintext error description
* @return The AiApiError instance.
*/
AiApiError message( @Nonnull final String message);
}

}

Loading

0 comments on commit e5424d6

Please sign in to comment.