Skip to content

Commit

Permalink
Merge branch 'main' into workflow/compatibility-version
Browse files Browse the repository at this point in the history
  • Loading branch information
newtork authored Nov 19, 2024
2 parents d1025ff + cf661ad commit d1bad94
Show file tree
Hide file tree
Showing 16 changed files with 224 additions and 99 deletions.
1 change: 1 addition & 0 deletions .github/workflows/continuous-integration.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ jobs:
with:
fetch-depth: 1
ref: ${{ github.event.pull_request.head.ref }}
repository: ${{ github.event.pull_request.head.repo.full_name }}

- name: "Setup java"
uses: actions/setup-java@v4
Expand Down
7 changes: 2 additions & 5 deletions .github/workflows/perform-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,6 @@ jobs:
with:
distribution: "sapmachine"
java-version: ${{ env.JAVA_VERSION }}
server-id: ossrh
server-username: MAVEN_CENTRAL_USER # env variable for username in deploy
server-password: MAVEN_CENTRAL_PASSWORD # env variable for token in deploy

- name: "Download Release Asset"
id: download-asset
Expand All @@ -113,8 +110,8 @@ jobs:
- name: "Deploy"
run: |
MVN_ARGS="${{ env.MVN_CLI_ARGS }} deploy -Drelease -s settings.xml"
mvn $MVN_ARGS
MVN_ARGS="${{ env.MVN_CLI_ARGS }} -Drelease -s settings.xml"
mvn deploy $MVN_ARGS
env:
MAVEN_GPG_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }}

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ After restarting your application, you should see an "aicore" entry in the `VCAP

- **Name**: `my-aicore`
- **Type**: `HTTP`
- **URL**: `[serviceurls.AI_API_URL]/v2` (append `/v2` to the URL)
- **URL**: `[serviceurls.AI_API_URL]`
- **Proxy Type**: `Internet`
- **Authentication**: `OAuth2ClientCredentials`
- **Client ID**: `[clientid]`
Expand Down
3 changes: 1 addition & 2 deletions docs/guides/ORCHESTRATION_CHAT_COMPLETION.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,7 @@ var prompt = new OrchestrationPrompt("Hello world! Why is this phrase so famous?

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

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

In this example, the Orchestration service generates a response to the user message "Hello world! Why is this phrase so famous?".
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.sap.ai.sdk.orchestration;

import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.sap.ai.sdk.orchestration.client.model.LLMChoice;
import com.sap.ai.sdk.orchestration.client.model.LLMModuleResultSynchronous;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;

@NoArgsConstructor(access = AccessLevel.PRIVATE)
final class JacksonMixins {
/** Mixin to enforce a specific subtype to be deserialized always. */
@JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
@JsonDeserialize(as = LLMModuleResultSynchronous.class)
interface LLMModuleResultMixIn {}

/** Mixin to enforce a specific subtype to be deserialized always. */
@JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
@JsonDeserialize(as = LLMChoice.class)
interface ModuleResultsOutputUnmaskingInnerMixIn {}

/** Mixin to suppress @JsonTypeInfo for oneOf interfaces. */
@JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
interface NoTypeInfoMixin {}
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.sap.ai.sdk.orchestration;

import static lombok.AccessLevel.PACKAGE;

import com.sap.ai.sdk.orchestration.client.model.CompletionPostResponse;
import com.sap.ai.sdk.orchestration.client.model.LLMModuleResultSynchronous;
import javax.annotation.Nonnull;
import lombok.RequiredArgsConstructor;
import lombok.Value;

/** Orchestration chat completion output. */
@Value
@RequiredArgsConstructor(access = PACKAGE)
public class OrchestrationChatResponse {
CompletionPostResponse originalResponse;

/**
* Get the message content from the output.
*
* <p>Note: If there are multiple choices only the first one is returned
*
* @return the message content or empty string.
* @throws OrchestrationClientException if the content filter filtered the output.
*/
@Nonnull
public String getContent() throws OrchestrationClientException {
final var choices =
((LLMModuleResultSynchronous) originalResponse.getOrchestrationResult()).getChoices();

if (choices.isEmpty()) {
return "";
}

final var choice = choices.get(0);

if ("content_filter".equals(choice.getFinishReason())) {
throw new OrchestrationClientException("Content filter filtered the output.");
}
return choice.getMessage().getContent();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,13 @@ public class OrchestrationClient {
.visibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.NONE)
.visibility(PropertyAccessor.SETTER, JsonAutoDetect.Visibility.NONE)
.serializationInclusion(JsonInclude.Include.NON_NULL)
.mixIn(LLMModuleResult.class, LLMModuleResultMixIn.class)
.mixIn(ModuleResultsOutputUnmaskingInner.class, NoTypeInfoMixin.class)
.mixIn(FilterConfig.class, NoTypeInfoMixin.class)
.mixIn(MaskingProviderConfig.class, NoTypeInfoMixin.class)
.mixIn(TemplatingModuleConfig.class, NoTypeInfoMixin.class)
.mixIn(LLMModuleResult.class, JacksonMixins.LLMModuleResultMixIn.class)
.mixIn(
ModuleResultsOutputUnmaskingInner.class,
JacksonMixins.ModuleResultsOutputUnmaskingInnerMixIn.class)
.mixIn(FilterConfig.class, JacksonMixins.NoTypeInfoMixin.class)
.mixIn(MaskingProviderConfig.class, JacksonMixins.NoTypeInfoMixin.class)
.mixIn(TemplatingModuleConfig.class, JacksonMixins.NoTypeInfoMixin.class)
.build();
}

Expand Down Expand Up @@ -99,12 +101,12 @@ public static CompletionPostRequest toCompletionPostRequest(
* @throws OrchestrationClientException if the request fails.
*/
@Nonnull
public CompletionPostResponse chatCompletion(
public OrchestrationChatResponse chatCompletion(
@Nonnull final OrchestrationPrompt prompt, @Nonnull final OrchestrationModuleConfig config)
throws OrchestrationClientException {

val request = toCompletionPostRequest(prompt, config);
return executeRequest(request);
return new OrchestrationChatResponse(executeRequest(request));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,7 @@ void testCompletion() {
final var result = client.chatCompletion(prompt, config);

assertThat(result).isNotNull();
var orchestrationResult = (LLMModuleResultSynchronous) result.getOrchestrationResult();
assertThat(orchestrationResult.getChoices().get(0).getMessage().getContent()).isNotEmpty();
assertThat(result.getContent()).isNotEmpty();
}

@Test
Expand All @@ -141,11 +140,12 @@ void testTemplating() throws IOException {
final var result =
client.chatCompletion(new OrchestrationPrompt(inputParams, template), config);

assertThat(result.getRequestId()).isEqualTo("26ea36b5-c196-4806-a9a6-a686f0c6ad91");
assertThat(result.getModuleResults().getTemplating().get(0).getContent())
final var response = result.getOriginalResponse();
assertThat(response.getRequestId()).isEqualTo("26ea36b5-c196-4806-a9a6-a686f0c6ad91");
assertThat(response.getModuleResults().getTemplating().get(0).getContent())
.isEqualTo("Reply with 'Orchestration Service is working!' in German");
assertThat(result.getModuleResults().getTemplating().get(0).getRole()).isEqualTo("user");
var llm = (LLMModuleResultSynchronous) result.getModuleResults().getLlm();
assertThat(response.getModuleResults().getTemplating().get(0).getRole()).isEqualTo("user");
var llm = (LLMModuleResultSynchronous) response.getModuleResults().getLlm();
assertThat(llm.getId()).isEqualTo("chatcmpl-9lzPV4kLrXjFckOp2yY454wksWBoj");
assertThat(llm.getObject()).isEqualTo("chat.completion");
assertThat(llm.getCreated()).isEqualTo(1721224505);
Expand All @@ -160,7 +160,7 @@ void testTemplating() throws IOException {
assertThat(usage.getCompletionTokens()).isEqualTo(7);
assertThat(usage.getPromptTokens()).isEqualTo(19);
assertThat(usage.getTotalTokens()).isEqualTo(26);
var orchestrationResult = (LLMModuleResultSynchronous) result.getOrchestrationResult();
var orchestrationResult = (LLMModuleResultSynchronous) response.getOrchestrationResult();
assertThat(orchestrationResult.getId()).isEqualTo("chatcmpl-9lzPV4kLrXjFckOp2yY454wksWBoj");
assertThat(orchestrationResult.getObject()).isEqualTo("chat.completion");
assertThat(orchestrationResult.getCreated()).isEqualTo(1721224505);
Expand Down Expand Up @@ -286,7 +286,8 @@ void messagesHistory() throws IOException {

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

assertThat(result.getRequestId()).isEqualTo("26ea36b5-c196-4806-a9a6-a686f0c6ad91");
assertThat(result.getOriginalResponse().getRequestId())
.isEqualTo("26ea36b5-c196-4806-a9a6-a686f0c6ad91");

// verify that the history is sent correctly
try (var requestInputStream = fileLoader.apply("messagesHistoryRequest.json")) {
Expand All @@ -298,7 +299,7 @@ void messagesHistory() throws IOException {
}

@Test
void maskingAnonymization() throws IOException {
void maskingPseudonymization() throws IOException {
stubFor(
post(urlPathEqualTo("/v2/inference/deployments/abcdef0123456789/completion"))
.willReturn(
Expand All @@ -307,18 +308,16 @@ void maskingAnonymization() throws IOException {
.withHeader("Content-Type", "application/json")));

final var maskingConfig =
createMaskingConfig(DPIConfig.MethodEnum.ANONYMIZATION, DPIEntities.PHONE);
createMaskingConfig(DPIConfig.MethodEnum.PSEUDONYMIZATION, DPIEntities.PHONE);

final var result = client.chatCompletion(prompt, config.withMaskingConfig(maskingConfig));
final var response = result.getOriginalResponse();

assertThat(result).isNotNull();
GenericModuleResult inputMasking = result.getModuleResults().getInputMasking();
assertThat(response).isNotNull();
GenericModuleResult inputMasking = response.getModuleResults().getInputMasking();
assertThat(inputMasking.getMessage()).isEqualTo("Input to LLM is masked successfully.");
assertThat(inputMasking.getData()).isNotNull();
final var choices = ((LLMModuleResultSynchronous) result.getOrchestrationResult()).getChoices();
assertThat(choices.get(0).getMessage().getContent())
.isEqualTo(
"I'm sorry, I cannot provide information about specific individuals, including their nationality.");
assertThat(result.getContent()).contains("Hi Mallory");

// verify that the request is sent correctly
try (var requestInputStream = fileLoader.apply("maskingRequest.json")) {
Expand Down Expand Up @@ -414,4 +413,17 @@ void testErrorHandling() {

softly.assertAll();
}

@Test
void testEmptyChoicesResponse() {
stubFor(
post(urlPathEqualTo("/v2/inference/deployments/abcdef0123456789/completion"))
.willReturn(
aResponse()
.withBodyFile("emptyChoicesResponse.json")
.withHeader("Content-Type", "application/json")));
final var result = client.chatCompletion(prompt, config);

assertThat(result.getContent()).isEmpty();
}
}
35 changes: 35 additions & 0 deletions orchestration/src/test/resources/__files/emptyChoicesResponse.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"request_id": "26ea36b5-c196-4806-a9a6-a686f0c6ad91",
"module_results": {
"templating": [
{
"role": "user",
"content": "Reply with 'Orchestration Service is working!' in German"
}
],
"llm": {
"id": "chatcmpl-9lzPV4kLrXjFckOp2yY454wksWBoj",
"object": "chat.completion",
"created": 1721224505,
"model": "gpt-35-turbo-16k",
"choices": [],
"usage": {
"completion_tokens": 7,
"prompt_tokens": 19,
"total_tokens": 26
}
}
},
"orchestration_result": {
"id": "chatcmpl-9lzPV4kLrXjFckOp2yY454wksWBoj",
"object": "chat.completion",
"created": 1721224505,
"model": "gpt-35-turbo-16k",
"choices": [],
"usage": {
"completion_tokens": 7,
"prompt_tokens": 19,
"total_tokens": 26
}
}
}
Loading

0 comments on commit d1bad94

Please sign in to comment.