Skip to content

Commit 8c86e91

Browse files
feat: Orchestration Grounding convenience (#293)
* Orchestration Grounding convenience * Added docs and release notes * Removed someId * Alex likes functional interfaces * final
1 parent 9bc2962 commit 8c86e91

File tree

8 files changed

+261
-76
lines changed

8 files changed

+261
-76
lines changed

docs/guides/ORCHESTRATION_CHAT_COMPLETION.md

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -206,31 +206,31 @@ In this example, the input will be masked before the call to the LLM and will re
206206
Use the grounding module to provide additional context to the AI model.
207207

208208
```java
209-
var message =
210-
Message.user(
211-
"{{?groundingInput}} Use the following information as additional context: {{?groundingOutput}}");
212-
var prompt =
213-
new OrchestrationPrompt(Map.of("groundingInput", "What does Joule do?"), message);
214-
215-
var filterInner =
216-
DocumentGroundingFilter.create().id("someID").dataRepositoryType(DataRepositoryType.VECTOR);
217-
var groundingConfigConfig =
218-
GroundingModuleConfigConfig.create()
219-
.inputParams(List.of("groundingInput"))
220-
.outputParam("groundingOutput")
221-
.addFiltersItem(filterInner);
222-
223-
var groundingConfig =
224-
GroundingModuleConfig.create()
225-
.type(GroundingModuleConfig.TypeEnum.DOCUMENT_GROUNDING_SERVICE)
226-
.config(groundingConfigConfig);
227-
var configWithGrounding = config.withGroundingConfig(groundingConfig);
228-
229-
var result =
230-
new OrchestrationClient().chatCompletion(prompt, configWithGrounding);
209+
// optional filter for collections
210+
var documentMetadata =
211+
SearchDocumentKeyValueListPair.create()
212+
.key("my-collection")
213+
.value("value")
214+
.addSelectModeItem(SearchSelectOptionEnum.IGNORE_IF_KEY_ABSENT);
215+
// optional filter for document chunks
216+
var databaseFilter =
217+
DocumentGroundingFilter.create()
218+
.id("")
219+
.dataRepositoryType(DataRepositoryType.VECTOR)
220+
.addDocumentMetadataItem(documentMetadata);
221+
222+
var groundingConfig = Grounding.create().filter(databaseFilter);
223+
var prompt = groundingConfig.createGroundingPrompt("What does Joule do?");
224+
var configWithGrounding = config.withGrounding(groundingConfig);
225+
226+
var result = client.chatCompletion(prompt, configWithGrounding);
231227
```
232228

233-
In this example, the AI model is provided with additional context in the form of grounding information. Note, that it is necessary to provide the grounding input via one or more input variables.
229+
In this example, the AI model is provided with additional context in the form of grounding information.
230+
231+
`Grounding.create()` is by default a document grounding service with a vector data repository.
232+
233+
Please find [an example in our Spring Boot application](../../sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/OrchestrationService.java).
234234

235235
## Stream chat completion
236236

docs/release-notes/release_notes.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
- New Orchestration features:
1818
- [Spring AI integration](../guides/ORCHESTRATION_CHAT_COMPLETION.md#spring-ai-integration)
19+
- [Add Grounding configuration convenience](../guides/ORCHESTRATION_CHAT_COMPLETION.md#grounding)
1920
- Images are now supported as input in newly introduced `MultiChatMessage`.
2021
- `MultiChatMessage` also allows for multiple content items (text or image) in one object.
2122
- Grounding input can be masked with `DPIConfig`.
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package com.sap.ai.sdk.orchestration;
2+
3+
import com.google.common.annotations.Beta;
4+
import com.sap.ai.sdk.orchestration.model.DataRepositoryType;
5+
import com.sap.ai.sdk.orchestration.model.DocumentGroundingFilter;
6+
import com.sap.ai.sdk.orchestration.model.GroundingModuleConfig;
7+
import com.sap.ai.sdk.orchestration.model.GroundingModuleConfig.TypeEnum;
8+
import com.sap.ai.sdk.orchestration.model.GroundingModuleConfigConfig;
9+
import com.sap.ai.sdk.orchestration.model.GroundingModuleConfigConfigFiltersInner;
10+
import java.util.List;
11+
import java.util.Map;
12+
import javax.annotation.Nonnull;
13+
import lombok.Setter;
14+
import lombok.experimental.Accessors;
15+
import lombok.val;
16+
17+
/**
18+
* Grounding integrates external, contextually relevant, domain-specific, or real-time data into AI
19+
* processes. This data supplements the natural language processing capabilities of pre-trained
20+
* models, which are trained on general material.
21+
*
22+
* @link <a href="https://help.sap.com/docs/sap-ai-core/sap-ai-core-service-guide/grounding">SAP AI
23+
* Core: Orchestration - Grounding</a>
24+
*/
25+
@Beta
26+
@Accessors(fluent = true)
27+
public class Grounding implements GroundingProvider {
28+
29+
@Nonnull
30+
private List<GroundingModuleConfigConfigFiltersInner> filters =
31+
List.of(
32+
DocumentGroundingFilter.create().id("").dataRepositoryType(DataRepositoryType.VECTOR));
33+
34+
@Setter(onMethod_ = {@Nonnull})
35+
private TypeEnum documentGroundingService = TypeEnum.DOCUMENT_GROUNDING_SERVICE;
36+
37+
/**
38+
* Create a new default grounding provider.
39+
*
40+
* <p>It is by default a document grounding service with a vector data repository.
41+
*
42+
* @return The grounding provider.
43+
*/
44+
@Nonnull
45+
public static Grounding create() {
46+
return new Grounding();
47+
}
48+
49+
/**
50+
* Set filters for grounding.
51+
*
52+
* @param filters List of filters to set.
53+
* @return The modified grounding configuration.
54+
*/
55+
@Nonnull
56+
public Grounding filters(@Nonnull final GroundingModuleConfigConfigFiltersInner... filters) {
57+
if (filters.length != 0) {
58+
this.filters = List.of(filters);
59+
}
60+
return this;
61+
}
62+
63+
/**
64+
* Create a prompt with grounding parameters included in the message.
65+
*
66+
* <p>It uses the inputParams {@code userMessage} for the user message and {@code
67+
* groundingContext} for the grounding context.
68+
*
69+
* @param message The user message.
70+
* @return The prompt with grounding.
71+
*/
72+
@Nonnull
73+
public OrchestrationPrompt createGroundingPrompt(@Nonnull final String message) {
74+
return new OrchestrationPrompt(
75+
Map.of("userMessage", message),
76+
Message.user(
77+
"{{?userMessage}} Use the following information as additional context: {{?groundingContext}}"));
78+
}
79+
80+
@Nonnull
81+
@Override
82+
public GroundingModuleConfig createConfig() {
83+
val groundingConfigConfig =
84+
GroundingModuleConfigConfig.create()
85+
.inputParams(List.of("userMessage"))
86+
.outputParam("groundingContext")
87+
.filters(filters);
88+
89+
return GroundingModuleConfig.create()
90+
.type(documentGroundingService)
91+
.config(groundingConfigConfig);
92+
}
93+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.sap.ai.sdk.orchestration;
2+
3+
import com.sap.ai.sdk.orchestration.model.GroundingModuleConfig;
4+
import javax.annotation.Nonnull;
5+
6+
/**
7+
* Interface for grounding configurations.
8+
*
9+
* @link <a href="https://help.sap.com/docs/sap-ai-core/sap-ai-core-service-guide/grounding">SAP AI
10+
* Core: Orchestration - Grounding</a>
11+
*/
12+
@FunctionalInterface
13+
public interface GroundingProvider {
14+
15+
/**
16+
* Create a grounding configuration.
17+
*
18+
* @return the grounding configuration
19+
*/
20+
@Nonnull
21+
GroundingModuleConfig createConfig();
22+
}

orchestration/src/main/java/com/sap/ai/sdk/orchestration/MaskingProvider.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@
1212
public interface MaskingProvider {
1313

1414
/**
15-
* Create a masking provider for the configuration.
15+
* Create a masking configuration.
1616
*
17-
* @return the masking provider
17+
* @return the masking configuration
1818
*/
1919
@Nonnull
2020
MaskingProviderConfig createConfig();

orchestration/src/main/java/com/sap/ai/sdk/orchestration/OrchestrationModuleConfig.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,4 +197,18 @@ public OrchestrationModuleConfig withOutputFiltering(
197197

198198
return this.withFilteringConfig(newFilteringConfig);
199199
}
200+
201+
/**
202+
* Creates a new configuration with the given grounding configuration.
203+
*
204+
* @link <a href="https://help.sap.com/docs/sap-ai-core/sap-ai-core-service-guide/grounding">SAP
205+
* AI Core: Orchestration - Grounding</a>
206+
* @param groundingProvider The grounding configuration to use.
207+
* @return A new configuration with the given grounding configuration.
208+
*/
209+
@Nonnull
210+
public OrchestrationModuleConfig withGrounding(
211+
@Nonnull final GroundingProvider groundingProvider) {
212+
return this.withGroundingConfig(groundingProvider.createConfig());
213+
}
200214
}

orchestration/src/test/java/com/sap/ai/sdk/orchestration/OrchestrationModuleConfigTest.java

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,17 @@
33
import static com.sap.ai.sdk.orchestration.AzureFilterThreshold.ALLOW_SAFE_LOW_MEDIUM;
44
import static com.sap.ai.sdk.orchestration.OrchestrationAiModel.GPT_4O;
55
import static com.sap.ai.sdk.orchestration.OrchestrationAiModel.Parameter.MAX_TOKENS;
6+
import static com.sap.ai.sdk.orchestration.model.DataRepositoryType.VECTOR;
7+
import static com.sap.ai.sdk.orchestration.model.GroundingModuleConfig.TypeEnum.DOCUMENT_GROUNDING_SERVICE;
68
import static org.assertj.core.api.Assertions.assertThat;
79
import static org.assertj.core.api.Assertions.assertThatThrownBy;
810

911
import com.sap.ai.sdk.orchestration.model.DPIConfig;
1012
import com.sap.ai.sdk.orchestration.model.DPIEntities;
13+
import com.sap.ai.sdk.orchestration.model.DocumentGroundingFilter;
14+
import com.sap.ai.sdk.orchestration.model.GroundingModuleConfigConfig;
15+
import com.sap.ai.sdk.orchestration.model.GroundingModuleConfigConfigFiltersInner;
16+
import java.util.List;
1117
import java.util.Map;
1218
import org.junit.jupiter.api.Test;
1319

@@ -121,4 +127,50 @@ void testLLMConfig() {
121127
.withFailMessage("Static models should be unchanged")
122128
.isEqualTo("latest");
123129
}
130+
131+
@Test
132+
void testGroundingConfig() {
133+
var groundingConfig = Grounding.create();
134+
var config =
135+
new OrchestrationModuleConfig().withLlmConfig(GPT_4O).withGrounding(groundingConfig);
136+
137+
assertThat(config.getGroundingConfig()).isNotNull();
138+
assertThat(config.getGroundingConfig().getType()).isEqualTo(DOCUMENT_GROUNDING_SERVICE);
139+
140+
GroundingModuleConfigConfig configConfig = config.getGroundingConfig().getConfig();
141+
assertThat(configConfig).isNotNull();
142+
assertThat(configConfig.getInputParams()).containsExactly("userMessage");
143+
assertThat(configConfig.getOutputParam()).isEqualTo("groundingContext");
144+
145+
List<GroundingModuleConfigConfigFiltersInner> filters = configConfig.getFilters();
146+
assertThat(filters).hasSize(1);
147+
DocumentGroundingFilter filter = (DocumentGroundingFilter) filters.get(0);
148+
assertThat(filter.getId()).isEqualTo("");
149+
assertThat(filter.getDataRepositoryType()).isEqualTo(VECTOR);
150+
}
151+
152+
@Test
153+
void testGroundingConfigWithFilters() {
154+
var filter1 = DocumentGroundingFilter.create().id("123").dataRepositoryType(VECTOR);
155+
var filter2 = DocumentGroundingFilter.create().id("234").dataRepositoryType(VECTOR);
156+
var groundingConfig = Grounding.create().filters(filter1, filter2);
157+
var config =
158+
new OrchestrationModuleConfig().withLlmConfig(GPT_4O).withGrounding(groundingConfig);
159+
160+
assertThat(config.getGroundingConfig()).isNotNull();
161+
var configConfig = config.getGroundingConfig().getConfig();
162+
assertThat(configConfig).isNotNull();
163+
164+
assertThat(config.getGroundingConfig().getConfig().getFilters()).hasSize(2);
165+
}
166+
167+
@Test
168+
void testGroundingPrompt() {
169+
var prompt = Grounding.create().createGroundingPrompt("Hello, World!");
170+
assertThat(prompt.getMessages()).hasSize(1);
171+
var message = prompt.getMessages().get(0);
172+
assertThat(message.content())
173+
.isEqualTo(
174+
"{{?userMessage}} Use the following information as additional context: {{?groundingContext}}");
175+
}
124176
}

0 commit comments

Comments
 (0)