Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
46f4ba4
WiP
CharlesDuboisSAP Jan 13, 2025
ebc21ff
WiP 2
CharlesDuboisSAP Jan 13, 2025
ed7c6e4
finito
CharlesDuboisSAP Jan 14, 2025
3cc0dd0
WiP
CharlesDuboisSAP Jan 13, 2025
c5a43a0
WiP 2
CharlesDuboisSAP Jan 13, 2025
d496c13
finito
CharlesDuboisSAP Jan 14, 2025
002f677
Merge remote-tracking branch 'origin/bean' into bean
CharlesDuboisSAP Jan 14, 2025
ccbcd64
Formatting
bot-sdk-js Jan 14, 2025
605a51d
docs
CharlesDuboisSAP Jan 14, 2025
7155ad5
Merge remote-tracking branch 'origin/bean' into bean
CharlesDuboisSAP Jan 14, 2025
d45a397
repo in parent pom
CharlesDuboisSAP Jan 14, 2025
8c751be
Formatting
bot-sdk-js Jan 14, 2025
e7e5a9d
Update docs
CharlesDuboisSAP Jan 14, 2025
ab63181
Merge remote-tracking branch 'origin/bean' into bean
CharlesDuboisSAP Jan 14, 2025
529b37e
Aligned headers
CharlesDuboisSAP Jan 14, 2025
d064734
Added no args constructor
CharlesDuboisSAP Jan 14, 2025
2d20234
Removed spring-ai-app
CharlesDuboisSAP Jan 15, 2025
15f4347
Merge branch 'refs/heads/main' into bean
CharlesDuboisSAP Jan 15, 2025
afa6f9f
Added images in index.html
CharlesDuboisSAP Jan 15, 2025
bc0f64a
Alex's review comments
CharlesDuboisSAP Jan 15, 2025
efe05e1
Shorter
CharlesDuboisSAP Jan 15, 2025
2133fc4
Added instructions
CharlesDuboisSAP Jan 15, 2025
6cdd6a7
M5
CharlesDuboisSAP Jan 15, 2025
6c5a512
fixed copy
CharlesDuboisSAP Jan 16, 2025
0fd5e10
Formatting
bot-sdk-js Jan 16, 2025
5088e7d
removed warnings
CharlesDuboisSAP Jan 16, 2025
6ade28f
green CI
CharlesDuboisSAP Jan 16, 2025
da7d72e
Fixed controller, added service
CharlesDuboisSAP Jan 16, 2025
be64b9e
Removed chat Memory
CharlesDuboisSAP Jan 16, 2025
5ffb8ec
Merge branch 'main' into bean
CharlesDuboisSAP Jan 17, 2025
5f6a163
Alex's review
CharlesDuboisSAP Jan 17, 2025
ad2b83b
Better documentation
CharlesDuboisSAP Jan 17, 2025
b94276d
Added images
CharlesDuboisSAP Jan 17, 2025
6f549d9
Better images
CharlesDuboisSAP Jan 17, 2025
46c84ca
mb
CharlesDuboisSAP Jan 17, 2025
6e49163
added note
CharlesDuboisSAP Jan 17, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion docs/release-notes/release_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@

### ✨ New Functionality

- [Integrated the Orchestration client in Spring AI.](../guides/ORCHESTRATION_CHAT_COMPLETION.md#spring-ai-integration)
- Orchestration supports images as input in newly introduced `MultiChatMessage`.
- `MultiChatMessage` also allows for multiple content items (text or image) in one object.
- Grounding input can be masked with `DPIConfig`.
- [Integrate the Orchestration client in Spring AI.](../guides/ORCHESTRATION_CHAT_COMPLETION.md#spring-ai-integration)

### 📈 Improvements

Expand Down
7 changes: 4 additions & 3 deletions orchestration/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@
</developers>
<properties>
<project.rootdir>${project.basedir}/../</project.rootdir>
<coverage.complexity>78%</coverage.complexity>
<coverage.line>91%</coverage.line>
<coverage.complexity>76%</coverage.complexity>
<coverage.line>90%</coverage.line>
<coverage.instruction>91%</coverage.instruction>
<coverage.branch>70%</coverage.branch>
<coverage.branch>69%</coverage.branch>
<coverage.method>70%</coverage.method>
<coverage.class>100%</coverage.class>
</properties>
Expand All @@ -57,6 +57,7 @@
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-core</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents.core5</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
import lombok.NoArgsConstructor;
import lombok.val;

/** Factory to create all data objects from an orchestration configuration. */
/** Internal factory to create all data objects from an orchestration configuration. */
@NoArgsConstructor(access = AccessLevel.NONE)
final class ConfigToRequestTransformer {
public final class ConfigToRequestTransformer {
@Nonnull
static CompletionPostRequest toCompletionPostRequest(
@Nonnull final OrchestrationPrompt prompt, @Nonnull final OrchestrationModuleConfig config) {
Expand Down Expand Up @@ -59,8 +59,9 @@ static TemplatingModuleConfig toTemplateModuleConfig(
return Template.create().template(messagesWithPrompt);
}

/** Internal method to convert the convenience configuration to the data objec configuration. */
@Nonnull
static ModuleConfigs toModuleConfigs(@Nonnull final OrchestrationModuleConfig config) {
public static ModuleConfigs toModuleConfigs(@Nonnull final OrchestrationModuleConfig config) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Major)

(I expect static code checks to flag warnings for) incomplete JavaDoc on public API.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see that we only need this for copy. Did you check if we really need to support copy? If not, we could think about throwing a NotImplementedException

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OrchestrationModuleConfig is immutable. I will turn the deep copy into a normal copy since config cannot be modified anyway.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But the config inside of OrchestrationModuleConfig can be modified, e.g. LlmModuleConfig. Shallow copy is only okay if we are sure it won't be modified.

val llmConfig =
Option.of(config.getLlmConfig())
.getOrElseThrow(() -> new IllegalStateException("LLM config is required."));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,20 @@
package com.sap.ai.sdk.orchestration;

import static com.sap.ai.sdk.core.JacksonConfiguration.getDefaultObjectMapper;
import static com.sap.ai.sdk.orchestration.OrchestrationJacksonConfiguration.getOrchestrationObjectMapper;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.annotations.Beta;
import com.sap.ai.sdk.core.AiCoreService;
import com.sap.ai.sdk.core.DeploymentResolutionException;
import com.sap.ai.sdk.core.common.ClientResponseHandler;
import com.sap.ai.sdk.core.common.ClientStreamingHandler;
import com.sap.ai.sdk.core.common.StreamedDelta;
import com.sap.ai.sdk.orchestration.model.ChatMessagesInner;
import com.sap.ai.sdk.orchestration.model.CompletionPostRequest;
import com.sap.ai.sdk.orchestration.model.CompletionPostResponse;
import com.sap.ai.sdk.orchestration.model.LLMModuleResult;
import com.sap.ai.sdk.orchestration.model.ModuleConfigs;
import com.sap.ai.sdk.orchestration.model.ModuleResultsOutputUnmaskingInner;
import com.sap.ai.sdk.orchestration.model.OrchestrationConfig;
import com.sap.cloud.sdk.cloudplatform.connectivity.ApacheHttpClient5Accessor;
import com.sap.cloud.sdk.cloudplatform.connectivity.HttpDestination;
Expand All @@ -41,25 +37,7 @@
public class OrchestrationClient {
private static final String DEFAULT_SCENARIO = "orchestration";

static final ObjectMapper JACKSON;

static {
JACKSON = getDefaultObjectMapper();

// Add mix-ins
JACKSON.addMixIn(LLMModuleResult.class, JacksonMixins.LLMModuleResultMixIn.class);
JACKSON.addMixIn(
ModuleResultsOutputUnmaskingInner.class,
JacksonMixins.ModuleResultsOutputUnmaskingInnerMixIn.class);

final var module =
new SimpleModule()
.addDeserializer(
ChatMessagesInner.class,
PolymorphicFallbackDeserializer.fromJsonSubTypes(ChatMessagesInner.class))
.setMixInAnnotation(ChatMessagesInner.class, JacksonMixins.NoneTypeInfoMixin.class);
JACKSON.registerModule(module);
}
static final ObjectMapper JACKSON = getOrchestrationObjectMapper();

@Nonnull private final Supplier<HttpDestination> destinationSupplier;

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

import static com.sap.ai.sdk.core.JacksonConfiguration.getDefaultObjectMapper;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.google.common.annotations.Beta;
import com.sap.ai.sdk.orchestration.model.ChatMessagesInner;
import com.sap.ai.sdk.orchestration.model.LLMModuleResult;
import com.sap.ai.sdk.orchestration.model.ModuleResultsOutputUnmaskingInner;
import javax.annotation.Nonnull;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.val;

/** Internal utility class for getting a default object mapper with preset configuration. */
@NoArgsConstructor(access = AccessLevel.NONE)
public class OrchestrationJacksonConfiguration {

/**
* Default object mapper used for JSON de-/serialization. <b>Only intended for internal usage
* within this SDK</b>. Largely follows the defaults set by Spring.
*
* @return A new object mapper with the default configuration.
* @see <a
* href="https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/converter/json/Jackson2ObjectMapperBuilder.html">Jackson2ObjectMapperBuilder</a>
*/
@Nonnull
@Beta
public static ObjectMapper getOrchestrationObjectMapper() {

val jackson = getDefaultObjectMapper();

// Add mix-ins
jackson.addMixIn(LLMModuleResult.class, JacksonMixins.LLMModuleResultMixIn.class);
jackson.addMixIn(
ModuleResultsOutputUnmaskingInner.class,
JacksonMixins.ModuleResultsOutputUnmaskingInnerMixIn.class);

final var module =
new SimpleModule()
.addDeserializer(
ChatMessagesInner.class,
PolymorphicFallbackDeserializer.fromJsonSubTypes(ChatMessagesInner.class))
.setMixInAnnotation(ChatMessagesInner.class, JacksonMixins.NoneTypeInfoMixin.class);
jackson.registerModule(module);
return jackson;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.sap.ai.sdk.orchestration.spring;

import com.google.common.annotations.Beta;
import com.sap.ai.sdk.orchestration.AssistantMessage;
import com.sap.ai.sdk.orchestration.OrchestrationClient;
import com.sap.ai.sdk.orchestration.OrchestrationPrompt;
Expand All @@ -17,13 +18,22 @@
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;

/** Spring AI integration for the orchestration service. */
/**
* Spring AI integration for the orchestration service.
*
* @since 1.2.0
*/
@Beta
@Slf4j
@RequiredArgsConstructor
public class OrchestrationChatModel implements ChatModel {
@Nonnull private OrchestrationClient client;

/** Default constructor. */
/**
* Default constructor.
*
* @since 1.2.0
*/
public OrchestrationChatModel() {
this.client = new OrchestrationClient();
}
Expand All @@ -32,12 +42,11 @@ public OrchestrationChatModel() {
@Override
public ChatResponse call(@Nonnull final Prompt prompt) {

if (prompt.getOptions() != null
&& prompt.getOptions() instanceof OrchestrationChatOptions options) {
if (prompt.getOptions() instanceof OrchestrationChatOptions options) {

val orchestrationPrompt = toOrchestrationPrompt(prompt);
val response = client.chatCompletion(orchestrationPrompt, options.getConfig());
return OrchestrationChatResponse.fromOrchestrationResponse(response.getOriginalResponse());
return new OrchestrationSpringChatResponse(response);
}
throw new IllegalArgumentException(
"Please add OrchestrationChatOptions to the Prompt: new Prompt(\"message\", new OrchestrationChatOptions(config))");
Expand All @@ -49,8 +58,6 @@ private OrchestrationPrompt toOrchestrationPrompt(@Nonnull final Prompt prompt)
return new OrchestrationPrompt(Map.of(), messages);
}

// endregion

@Nonnull
private static com.sap.ai.sdk.orchestration.Message[] toOrchestrationMessages(
@Nonnull final List<Message> messages) {
Expand Down
Loading
Loading