Skip to content

Commit a25007d

Browse files
Added e2e test + docs
1 parent e88fa85 commit a25007d

File tree

3 files changed

+61
-15
lines changed

3 files changed

+61
-15
lines changed

docs/guides/SPRING_AI_INTEGRATION.md

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,7 @@ Prompt prompt = new Prompt("What is the capital of France?", opts);
5050
ChatResponse response = client.call(prompt);
5151
```
5252

53-
Please
54-
find [an example in our Spring Boot application](../../sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/controllers/SpringAiOrchestrationController.java).
53+
Please find [an example in our Spring Boot application](../../sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/controllers/SpringAiOrchestrationController.java).
5554

5655
## Orchestration Masking
5756

@@ -76,3 +75,27 @@ ChatResponse response = client.call(prompt);
7675

7776
Please
7877
find [an example in our Spring Boot application](../../sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/controllers/SpringAiOrchestrationController.java).
78+
79+
## Stream chat completion
80+
81+
It's possible to pass a stream of chat completion delta elements, e.g. from the application backend
82+
to the frontend in real-time.
83+
84+
```java
85+
ChatModel client = new OrchestrationChatModel();
86+
OrchestrationModuleConfig config = new OrchestrationModuleConfig().withLlmConfig(GPT_35_TURBO);
87+
OrchestrationChatOptions opts = new OrchestrationChatOptions(config);
88+
89+
Prompt prompt =
90+
new Prompt(
91+
"Can you give me the first 100 numbers of the Fibonacci sequence?", opts);
92+
Flux<ChatResponse> flux = client.stream(prompt);
93+
94+
// also possible to keep only the chat completion text
95+
Flux<String> responseFlux =
96+
flux.map(chatResponse -> chatResponse.getResult().getOutput().getContent());
97+
```
98+
99+
_Note: A Spring endpoint can return `Flux` instead of `ResponseEntity`._
100+
101+
Please find [an example in our Spring Boot application](../../sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/controllers/SpringAiOrchestrationController.java).

sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/SpringAiOrchestrationService.java

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,19 @@ public ChatResponse completion() {
3737
return client.call(prompt);
3838
}
3939

40+
/**
41+
* Asynchronous stream of an OpenAI chat request
42+
*
43+
* @return a stream of assistant message responses
44+
*/
45+
@Nonnull
46+
public Flux<ChatResponse> streamChatCompletion() {
47+
val prompt =
48+
new Prompt(
49+
"Can you give me the first 100 numbers of the Fibonacci sequence?", defaultOptions);
50+
return client.stream(prompt);
51+
}
52+
4053
/**
4154
* Chat request to OpenAI through the Orchestration service with a template.
4255
*
@@ -74,17 +87,4 @@ public ChatResponse masking() {
7487

7588
return client.call(prompt);
7689
}
77-
78-
/**
79-
* Asynchronous stream of an OpenAI chat request
80-
*
81-
* @return a stream of assistant message responses
82-
*/
83-
@Nonnull
84-
public Flux<ChatResponse> streamChatCompletion() {
85-
val prompt =
86-
new Prompt(
87-
"Can you give me the first 100 numbers of the Fibonacci sequence?", defaultOptions);
88-
return client.stream(prompt);
89-
}
9090
}

sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/SpringAiOrchestrationTest.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@
33
import static org.assertj.core.api.Assertions.assertThat;
44

55
import com.sap.ai.sdk.app.services.SpringAiOrchestrationService;
6+
import java.util.concurrent.atomic.AtomicInteger;
7+
import lombok.extern.slf4j.Slf4j;
68
import org.junit.jupiter.api.Test;
79
import org.springframework.ai.chat.model.ChatResponse;
810

11+
@Slf4j
912
public class SpringAiOrchestrationTest {
1013

1114
SpringAiOrchestrationService service = new SpringAiOrchestrationService();
@@ -17,6 +20,26 @@ void testCompletion() {
1720
assertThat(response.getResult().getOutput().getContent()).contains("Paris");
1821
}
1922

23+
@Test
24+
void testStreamChatCompletion() {
25+
final var stream = service.streamChatCompletion().toStream();
26+
27+
final var filledDeltaCount = new AtomicInteger(0);
28+
stream
29+
// foreach consumes all elements, closing the stream at the end
30+
.forEach(
31+
delta -> {
32+
log.info("delta: {}", delta);
33+
if (!delta.getResult().getOutput().getContent().isEmpty()) {
34+
filledDeltaCount.incrementAndGet();
35+
}
36+
});
37+
38+
// the first two and the last delta don't have any content
39+
// see OpenAiChatCompletionDelta#getDeltaContent
40+
assertThat(filledDeltaCount.get()).isGreaterThan(0);
41+
}
42+
2043
@Test
2144
void testTemplate() {
2245
ChatResponse response = service.template();

0 commit comments

Comments
 (0)