Skip to content

Commit 8d5da14

Browse files
authored
feat: [Orchestration] Allow for streaming configuration convenience API (#561)
* Initial * Add test * Add release note * Add external link to javadoc * Fix PMD * Initial convenience class * Initial convenience class * Disable default stream * Update release note * JavaDoc fix
1 parent 02636e7 commit 8d5da14

File tree

7 files changed

+289
-60
lines changed

7 files changed

+289
-60
lines changed

docs/release_notes.md

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

4646
### ✨ New Functionality
4747

48+
- [Orchestration] For streaming, add convenience configuration for output-filter-overlap, chunk-size, and delimiters via `OrchestrationModuleConfig#withStreamConfig`.
4849
- [Orchestration] Added embedding generation support with new `OrchestrationClient#embed()` methods.
4950
- Added `OrchestrationEmbeddingModel` with `TEXT_EMBEDDING_3_SMALL`, `TEXT_EMBEDDING_3_LARGE`, `AMAZON_TITAN_EMBED_TEXT` and `NVIDIA_LLAMA_32_NV_EMBEDQA_1B` embedding models.
5051
- Introduced `OrchestrationEmbeddingRequest` for building requests fluently and `OrchestrationEmbeddingResponse#getEmbeddingVectors()` to retrieve embeddings.

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,11 @@ static CompletionRequestConfiguration toCompletionPostRequest(
3838

3939
val moduleConfigs = toModuleConfigs(configCopy);
4040

41+
val reqConfig =
42+
OrchestrationConfig.create().modules(moduleConfigs).stream(config.getGlobalStreamOptions());
43+
4144
return CompletionRequestConfiguration.create()
42-
.config(OrchestrationConfig.create().modules(moduleConfigs))
45+
.config(reqConfig)
4346
.placeholderValues(prompt.getTemplateParameters())
4447
.messagesHistory(messageHistory);
4548
}

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,13 @@ public OrchestrationChatResponse executeRequestFromJsonModuleConfig(
220220
@Nonnull
221221
public Stream<OrchestrationChatCompletionDelta> streamChatCompletionDeltas(
222222
@Nonnull final CompletionRequestConfiguration request) throws OrchestrationClientException {
223-
request.getConfig().setStream(GlobalStreamOptions.create().enabled(true).delimiters(null));
223+
val config = request.getConfig();
224+
val stream = config.getStream();
225+
if (stream == null) {
226+
config.setStream(GlobalStreamOptions.create().enabled(true).delimiters(null));
227+
} else {
228+
stream.enabled(true);
229+
}
224230

225231
return executor.stream(COMPLETION_ENDPOINT, request, customHeaders);
226232
}

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

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import com.google.common.annotations.Beta;
44
import com.sap.ai.sdk.orchestration.model.FilteringModuleConfig;
5+
import com.sap.ai.sdk.orchestration.model.FilteringStreamOptions;
6+
import com.sap.ai.sdk.orchestration.model.GlobalStreamOptions;
57
import com.sap.ai.sdk.orchestration.model.GroundingModuleConfig;
68
import com.sap.ai.sdk.orchestration.model.InputFilteringConfig;
79
import com.sap.ai.sdk.orchestration.model.LLMModelDetails;
@@ -18,6 +20,7 @@
1820
import javax.annotation.Nullable;
1921
import lombok.AccessLevel;
2022
import lombok.AllArgsConstructor;
23+
import lombok.Getter;
2124
import lombok.NoArgsConstructor;
2225
import lombok.Value;
2326
import lombok.With;
@@ -101,6 +104,18 @@ public class OrchestrationModuleConfig {
101104

102105
@Nullable SAPDocumentTranslationOutput outputTranslationConfig;
103106

107+
/** Configuration of optional streaming options for output filtering. */
108+
@With(AccessLevel.NONE) // may be exposed to public in the future
109+
@Getter(AccessLevel.PACKAGE)
110+
@Nullable
111+
FilteringStreamOptions outputFilteringStreamOptions;
112+
113+
/** Configuration of optional global streaming options, e.g. chunk-size. */
114+
@With(AccessLevel.PRIVATE) // may be exposed to public in the future
115+
@Getter(AccessLevel.PACKAGE)
116+
@Nullable
117+
GlobalStreamOptions globalStreamOptions;
118+
104119
/**
105120
* Creates a new configuration with the given LLM configuration.
106121
*
@@ -116,6 +131,19 @@ public OrchestrationModuleConfig withLlmConfig(@Nonnull final OrchestrationAiMod
116131
return withLlmConfig(aiModel.createConfig());
117132
}
118133

134+
/**
135+
* Creates a new configuration with the given stream configuration.
136+
*
137+
* @param config The stream configuration to use.
138+
* @return A new configuration with the given stream configuration.
139+
*/
140+
@Nonnull
141+
public OrchestrationModuleConfig withStreamConfig(
142+
@Nonnull final OrchestrationStreamConfig config) {
143+
return this.withOutputFilteringStreamOptions(config.createFilteringStreamOptions())
144+
.withGlobalStreamOptions(config.createGlobalStreamOptions());
145+
}
146+
119147
/**
120148
* Creates a new configuration with the given Data Masking configuration.
121149
*
@@ -204,7 +232,10 @@ public OrchestrationModuleConfig withOutputFiltering(
204232
.map(ContentFilter::createOutputFilterConfig)
205233
.toList();
206234

207-
final var outputFilter = OutputFilteringConfig.create().filters(filterConfigs);
235+
final var outputFilter =
236+
OutputFilteringConfig.create()
237+
.filters(filterConfigs)
238+
.streamOptions(outputFilteringStreamOptions);
208239

209240
final var newFilteringConfig =
210241
FilteringModuleConfig.create()
@@ -214,6 +245,33 @@ public OrchestrationModuleConfig withOutputFiltering(
214245
return this.withFilteringConfig(newFilteringConfig);
215246
}
216247

248+
/**
249+
* Creates a new configuration with the given output filtering stream options.
250+
*
251+
* @see <a
252+
* href="https://help.sap.com/docs/sap-ai-core/sap-ai-core-service-guide/streaming">Orchestration
253+
* documentation on streaming.</a>
254+
* @param outputFilteringStreamOptions The output filtering stream options to use.
255+
* @return A new configuration with the given output filtering stream options.
256+
*/
257+
@Nonnull
258+
OrchestrationModuleConfig withOutputFilteringStreamOptions(
259+
@Nullable final FilteringStreamOptions outputFilteringStreamOptions) {
260+
if (filteringConfig != null && filteringConfig.getOutput() != null) {
261+
filteringConfig.getOutput().setStreamOptions(outputFilteringStreamOptions);
262+
}
263+
return new OrchestrationModuleConfig(
264+
this.llmConfig,
265+
this.templateConfig,
266+
this.maskingConfig,
267+
this.filteringConfig,
268+
this.groundingConfig,
269+
this.inputTranslationConfig,
270+
this.outputTranslationConfig,
271+
outputFilteringStreamOptions,
272+
this.globalStreamOptions);
273+
}
274+
217275
/**
218276
* Creates a new configuration with the given grounding configuration.
219277
*
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package com.sap.ai.sdk.orchestration;
2+
3+
import com.sap.ai.sdk.orchestration.model.FilteringStreamOptions;
4+
import com.sap.ai.sdk.orchestration.model.GlobalStreamOptions;
5+
import java.util.List;
6+
import java.util.Optional;
7+
import javax.annotation.Nullable;
8+
import lombok.AccessLevel;
9+
import lombok.AllArgsConstructor;
10+
import lombok.Value;
11+
import lombok.With;
12+
import lombok.val;
13+
14+
/**
15+
* Configuration for orchestration streaming options.
16+
*
17+
* @since 1.12.0
18+
*/
19+
@Value
20+
@With
21+
@AllArgsConstructor(access = AccessLevel.PRIVATE)
22+
public class OrchestrationStreamConfig {
23+
/**
24+
* Number of characters that should be additionally sent to content filtering services from
25+
* previous chunks as additional context.
26+
*/
27+
@Nullable Integer filterOverlap;
28+
29+
/** Size of the chunks the response will be split into when streaming. */
30+
@Nullable Integer chunkSize;
31+
32+
/** List of delimiters to use for chunking the response when streaming. */
33+
@Nullable List<String> delimiters;
34+
35+
/** Default constructor for OrchestrationStreamConfig. */
36+
public OrchestrationStreamConfig() {
37+
this(null, null, null);
38+
}
39+
40+
@Nullable
41+
FilteringStreamOptions createFilteringStreamOptions() {
42+
return filterOverlap == null ? null : FilteringStreamOptions.create().overlap(filterOverlap);
43+
}
44+
45+
@Nullable
46+
GlobalStreamOptions createGlobalStreamOptions() {
47+
if (chunkSize == null && delimiters == null) {
48+
return null;
49+
}
50+
val opts = GlobalStreamOptions.create();
51+
Optional.ofNullable(chunkSize).ifPresent(opts::setChunkSize);
52+
opts.setDelimiters(delimiters == null ? null : List.copyOf(delimiters));
53+
return opts;
54+
}
55+
}

0 commit comments

Comments
 (0)