From 95d0328567bb06ed121510339277917469a2758c Mon Sep 17 00:00:00 2001 From: Charles Dubois <103174266+CharlesDuboisSAP@users.noreply.github.com> Date: Tue, 17 Sep 2024 16:39:50 +0200 Subject: [PATCH] Simple OpenAI API (#47) * OpenAI API proposal * Added convenience getContent * Applied Alex's suggestions * Added tests * Update foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiClient.java Co-authored-by: Matthias Kuhr <52661546+MatKuhr@users.noreply.github.com> * Throw in getContent * Added withSystemPrompt(String) and addMessages(...) * Updated README * PMD --------- Co-authored-by: Matthias Kuhr <52661546+MatKuhr@users.noreply.github.com> --- README.md | 23 ++-- .../sdk/app/controllers/OpenAiController.java | 38 ++---- .../ai/sdk/app/controllers/OpenAiTest.java | 3 +- .../foundationmodels/openai/OpenAiClient.java | 53 +++++++- .../model/OpenAiChatCompletionOutput.java | 21 +++ .../model/OpenAiChatCompletionParameters.java | 17 ++- .../openai/OpenAiClientTest.java | 124 ++++++++++++++---- .../OpenAiChatCompletionParametersTest.java | 2 +- 8 files changed, 216 insertions(+), 65 deletions(-) diff --git a/README.md b/README.md index 972872de..4fcd7227 100644 --- a/README.md +++ b/README.md @@ -181,18 +181,29 @@ See [an example pom in our Spring Boot application](e2e-test-app/pom.xml) ### Simple chat completion +```java +final OpenAiChatCompletionOutput result = + OpenAiClient.forModel(GPT_35_TURBO) + .withSystemPrompt("You are a helpful AI") + .chatCompletion("Hello World! Why is this phrase so famous?"); + +final String resultMessage = result.getContent(); +``` + +### Message history + ```java final var systemMessage = new OpenAiChatSystemMessage().setContent("You are a helpful assistant"); final var userMessage = new OpenAiChatUserMessage().addText("Hello World! Why is this phrase so famous?"); final var request = - new OpenAiChatCompletionParameters().setMessages(List.of(systemMessage, userMessage)); + new OpenAiChatCompletionParameters().addMessages(systemMessage, userMessage); final OpenAiChatCompletionOutput result = OpenAiClient.forModel(GPT_35_TURBO).chatCompletion(request); -final String resultMessage = result.getChoices().get(0).getMessage().getContent(); +final String resultMessage = result.getContent(); ``` See [an example in our Spring Boot application](e2e-test-app/src/main/java/com/sap/ai/sdk/app/controllers/OpenAiController.java) @@ -213,14 +224,10 @@ This is a blocking example for streaming and printing directly to the console: ```java String msg = "Can you give me the first 100 numbers of the Fibonacci sequence?"; -OpenAiChatCompletionParameters request = - new OpenAiChatCompletionParameters() - .setMessages(List.of(new OpenAiChatUserMessage().addText(msg))); - OpenAiClient client = OpenAiClient.forModel(GPT_35_TURBO); // try-with-resources on stream ensures the connection will be closed -try( Stream stream = client.streamChatCompletion(request)) { +try( Stream stream = client.streamChatCompletion(msg)) { stream.forEach(deltaString -> { System.out.print(deltaString); System.out.flush(); @@ -239,7 +246,7 @@ String msg = "Can you give me the first 100 numbers of the Fibonacci sequence?"; OpenAiChatCompletionParameters request = new OpenAiChatCompletionParameters() - .setMessages(List.of(new OpenAiChatUserMessage().addText(msg))); + .addMessages(new OpenAiChatUserMessage().addText(msg)); OpenAiChatCompletionOutput totalOutput = new OpenAiChatCompletionOutput(); OpenAiClient client = OpenAiClient.forModel(GPT_35_TURBO); diff --git a/e2e-test-app/src/main/java/com/sap/ai/sdk/app/controllers/OpenAiController.java b/e2e-test-app/src/main/java/com/sap/ai/sdk/app/controllers/OpenAiController.java index 2e563a50..5511ac8b 100644 --- a/e2e-test-app/src/main/java/com/sap/ai/sdk/app/controllers/OpenAiController.java +++ b/e2e-test-app/src/main/java/com/sap/ai/sdk/app/controllers/OpenAiController.java @@ -41,11 +41,7 @@ class OpenAiController { @GetMapping("/chatCompletion") @Nonnull public static OpenAiChatCompletionOutput chatCompletion() { - final var request = - new OpenAiChatCompletionParameters() - .setMessages(List.of(new OpenAiChatUserMessage().addText("Who is the prettiest"))); - - return OpenAiClient.forModel(GPT_35_TURBO).chatCompletion(request); + return OpenAiClient.forModel(GPT_35_TURBO).chatCompletion("Who is the prettiest"); } /** @@ -59,8 +55,7 @@ public static OpenAiChatCompletionOutput chatCompletion() { public static ResponseEntity streamChatCompletionDeltas() { final var msg = "Can you give me the first 100 numbers of the Fibonacci sequence?"; final var request = - new OpenAiChatCompletionParameters() - .setMessages(List.of(new OpenAiChatUserMessage().addText(msg))); + new OpenAiChatCompletionParameters().addMessages(new OpenAiChatUserMessage().addText(msg)); final var stream = OpenAiClient.forModel(GPT_35_TURBO).streamChatCompletionDeltas(request); @@ -103,15 +98,11 @@ private static String objectToJson(@Nonnull final Object obj) { @GetMapping("/streamChatCompletion") @Nonnull public static ResponseEntity streamChatCompletion() { - final var request = - new OpenAiChatCompletionParameters() - .setMessages( - List.of( - new OpenAiChatUserMessage() - .addText( - "Can you give me the first 100 numbers of the Fibonacci sequence?"))); - - final var stream = OpenAiClient.forModel(GPT_35_TURBO).streamChatCompletion(request); + final var stream = + OpenAiClient.forModel(GPT_35_TURBO) + .withSystemPrompt("Be a good, honest AI and answer the following question:") + .streamChatCompletion( + "Can you give me the first 100 numbers of the Fibonacci sequence?"); final var emitter = new ResponseBodyEmitter(); @@ -150,13 +141,12 @@ private static void send( public static OpenAiChatCompletionOutput chatCompletionImage() { final var request = new OpenAiChatCompletionParameters() - .setMessages( - List.of( - new OpenAiChatUserMessage() - .addText("Describe the following image.") - .addImage( - "https://upload.wikimedia.org/wikipedia/commons/thumb/5/59/SAP_2011_logo.svg/440px-SAP_2011_logo.svg.png", - ImageDetailLevel.HIGH))); + .addMessages( + new OpenAiChatUserMessage() + .addText("Describe the following image.") + .addImage( + "https://upload.wikimedia.org/wikipedia/commons/thumb/5/59/SAP_2011_logo.svg/440px-SAP_2011_logo.svg.png", + ImageDetailLevel.HIGH)); return OpenAiClient.forModel(GPT_4O).chatCompletion(request); } @@ -180,7 +170,7 @@ public static OpenAiChatCompletionOutput chatCompletionTools() { final var tool = new OpenAiChatCompletionTool().setType(FUNCTION).setFunction(function); final var request = new OpenAiChatCompletionParameters() - .setMessages(List.of(new OpenAiChatUserMessage().addText(question))) + .addMessages(new OpenAiChatUserMessage().addText(question)) .setTools(List.of(tool)) .setToolChoiceFunction("fibonacci"); diff --git a/e2e-test-app/src/test/java/com/sap/ai/sdk/app/controllers/OpenAiTest.java b/e2e-test-app/src/test/java/com/sap/ai/sdk/app/controllers/OpenAiTest.java index 2973239a..e4b1a6a7 100644 --- a/e2e-test-app/src/test/java/com/sap/ai/sdk/app/controllers/OpenAiTest.java +++ b/e2e-test-app/src/test/java/com/sap/ai/sdk/app/controllers/OpenAiTest.java @@ -7,7 +7,6 @@ import com.sap.ai.sdk.foundationmodels.openai.model.OpenAiChatCompletionOutput; import com.sap.ai.sdk.foundationmodels.openai.model.OpenAiChatCompletionParameters; import com.sap.ai.sdk.foundationmodels.openai.model.OpenAiChatMessage.OpenAiChatUserMessage; -import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Test; @@ -36,7 +35,7 @@ void chatCompletionImage() { void streamChatCompletion() { final var request = new OpenAiChatCompletionParameters() - .setMessages(List.of(new OpenAiChatUserMessage().addText("Who is the prettiest?"))); + .addMessages(new OpenAiChatUserMessage().addText("Who is the prettiest?")); final var totalOutput = new OpenAiChatCompletionOutput(); final var filledDeltaCount = new AtomicInteger(0); diff --git a/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiClient.java b/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiClient.java index 333ffbed..819d01f2 100644 --- a/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiClient.java +++ b/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiClient.java @@ -10,6 +10,8 @@ import com.sap.ai.sdk.foundationmodels.openai.model.OpenAiChatCompletionDelta; import com.sap.ai.sdk.foundationmodels.openai.model.OpenAiChatCompletionOutput; import com.sap.ai.sdk.foundationmodels.openai.model.OpenAiChatCompletionParameters; +import com.sap.ai.sdk.foundationmodels.openai.model.OpenAiChatMessage.OpenAiChatSystemMessage; +import com.sap.ai.sdk.foundationmodels.openai.model.OpenAiChatMessage.OpenAiChatUserMessage; import com.sap.ai.sdk.foundationmodels.openai.model.OpenAiEmbeddingOutput; import com.sap.ai.sdk.foundationmodels.openai.model.OpenAiEmbeddingParameters; import com.sap.ai.sdk.foundationmodels.openai.model.StreamedDelta; @@ -34,6 +36,7 @@ public final class OpenAiClient { private static final String DEFAULT_API_VERSION = "2024-02-01"; static final ObjectMapper JACKSON; + private String systemPrompt = null; static { JACKSON = @@ -95,6 +98,36 @@ public static OpenAiClient withCustomDestination(@Nonnull final Destination dest return new OpenAiClient(destination); } + /** + * Add a system prompt before user prompts. + * + * @param systemPrompt the system prompt + * @return the client + */ + @Nonnull + public OpenAiClient withSystemPrompt(@Nonnull final String systemPrompt) { + this.systemPrompt = systemPrompt; + return this; + } + + /** + * Generate a completion for the given user prompt. + * + * @param prompt a text message. + * @return the completion output + * @throws OpenAiClientException if the request fails + */ + @Nonnull + public OpenAiChatCompletionOutput chatCompletion(@Nonnull final String prompt) + throws OpenAiClientException { + final OpenAiChatCompletionParameters parameters = new OpenAiChatCompletionParameters(); + if (systemPrompt != null) { + parameters.addMessages(new OpenAiChatSystemMessage().setContent(systemPrompt)); + } + parameters.addMessages(new OpenAiChatUserMessage().addText(prompt)); + return chatCompletion(parameters); + } + /** * Generate a completion for the given prompt. * @@ -105,19 +138,25 @@ public static OpenAiClient withCustomDestination(@Nonnull final Destination dest @Nonnull public OpenAiChatCompletionOutput chatCompletion( @Nonnull final OpenAiChatCompletionParameters parameters) throws OpenAiClientException { + warnIfUnsupportedUsage(); return execute("/chat/completions", parameters, OpenAiChatCompletionOutput.class); } /** * Generate a completion for the given prompt. * - * @param parameters the prompt, including messages and other parameters. + * @param prompt a text message. * @return A stream of message deltas * @throws OpenAiClientException if the request fails or if the finish reason is content_filter */ @Nonnull - public Stream streamChatCompletion( - @Nonnull final OpenAiChatCompletionParameters parameters) throws OpenAiClientException { + public Stream streamChatCompletion(@Nonnull final String prompt) + throws OpenAiClientException { + final OpenAiChatCompletionParameters parameters = new OpenAiChatCompletionParameters(); + if (systemPrompt != null) { + parameters.addMessages(new OpenAiChatSystemMessage().setContent(systemPrompt)); + } + parameters.addMessages(new OpenAiChatUserMessage().addText(prompt)); return streamChatCompletionDeltas(parameters) .peek(OpenAiClient::throwOnContentFilter) .map(OpenAiChatCompletionDelta::getDeltaContent); @@ -140,10 +179,18 @@ private static void throwOnContentFilter(@Nonnull final OpenAiChatCompletionDelt @Nonnull public Stream streamChatCompletionDeltas( @Nonnull final OpenAiChatCompletionParameters parameters) throws OpenAiClientException { + warnIfUnsupportedUsage(); parameters.enableStreaming(); return executeStream("/chat/completions", parameters, OpenAiChatCompletionDelta.class); } + private void warnIfUnsupportedUsage() { + if (systemPrompt != null) { + log.warn( + "Previously set messages will be ignored, set it as an argument of this method instead."); + } + } + /** * Get a vector representation of a given input that can be easily consumed by machine learning * models and algorithms. diff --git a/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/model/OpenAiChatCompletionOutput.java b/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/model/OpenAiChatCompletionOutput.java index 449f3e8f..7d172aa0 100644 --- a/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/model/OpenAiChatCompletionOutput.java +++ b/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/model/OpenAiChatCompletionOutput.java @@ -1,8 +1,10 @@ package com.sap.ai.sdk.foundationmodels.openai.model; import com.fasterxml.jackson.annotation.JsonProperty; +import com.sap.ai.sdk.foundationmodels.openai.OpenAiClientException; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import javax.annotation.Nonnull; import lombok.EqualsAndHashCode; import lombok.Getter; @@ -28,6 +30,25 @@ public class OpenAiChatCompletionOutput extends OpenAiCompletionOutput @Getter(onMethod_ = @Nonnull) private String systemFingerprint; + /** + * Get the message content from the output. + * + *

Note: If there are multiple choices only the first one is returned + * + * @return the message content or empty string. + * @throws OpenAiClientException if the content filter filtered the output. + */ + @Nonnull + public String getContent() throws OpenAiClientException { + if (getChoices().isEmpty()) { + return ""; + } + if ("content_filter".equals(getChoices().get(0).getFinishReason())) { + throw new OpenAiClientException("Content filter filtered the output."); + } + return Objects.requireNonNullElse(getChoices().get(0).getMessage().getContent(), ""); + } + /** * Add a streamed delta to the total output. * diff --git a/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/model/OpenAiChatCompletionParameters.java b/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/model/OpenAiChatCompletionParameters.java index 19a3c425..f11a1697 100644 --- a/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/model/OpenAiChatCompletionParameters.java +++ b/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/model/OpenAiChatCompletionParameters.java @@ -6,6 +6,7 @@ import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonValue; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import javax.annotation.Nonnull; @@ -25,7 +26,6 @@ public class OpenAiChatCompletionParameters extends OpenAiCompletionParameters { /** A list of messages comprising the conversation so far. */ @JsonProperty("messages") - @Setter(onParam_ = @Nonnull) private List messages; /** @@ -197,4 +197,19 @@ private record Function(@JsonProperty("name") @Nonnull String name) {} public OpenAiChatCompletionParameters setStop(@Nullable final String... values) { return (OpenAiChatCompletionParameters) super.setStop(values); } + + /** + * Add messages to the conversation. + * + * @param messages The messages to add. + * @return this instance for chaining. + */ + @Nonnull + public OpenAiChatCompletionParameters addMessages(@Nonnull final OpenAiChatMessage... messages) { + if (this.messages == null) { + this.messages = new ArrayList<>(); + } + this.messages.addAll(Arrays.asList(messages)); + return this; + } } diff --git a/foundation-models/openai/src/test/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiClientTest.java b/foundation-models/openai/src/test/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiClientTest.java index aebc3718..3fdce3ca 100644 --- a/foundation-models/openai/src/test/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiClientTest.java +++ b/foundation-models/openai/src/test/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiClientTest.java @@ -18,7 +18,7 @@ import com.sap.ai.sdk.foundationmodels.openai.model.OpenAiChatCompletionDelta; import com.sap.ai.sdk.foundationmodels.openai.model.OpenAiChatCompletionOutput; import com.sap.ai.sdk.foundationmodels.openai.model.OpenAiChatCompletionParameters; -import com.sap.ai.sdk.foundationmodels.openai.model.OpenAiChatMessage; +import com.sap.ai.sdk.foundationmodels.openai.model.OpenAiChatMessage.OpenAiChatSystemMessage; import com.sap.ai.sdk.foundationmodels.openai.model.OpenAiChatMessage.OpenAiChatUserMessage; import com.sap.ai.sdk.foundationmodels.openai.model.OpenAiContentFilterPromptResults; import com.sap.ai.sdk.foundationmodels.openai.model.OpenAiEmbeddingParameters; @@ -30,9 +30,11 @@ import java.io.InputStream; import java.util.List; import java.util.Objects; +import java.util.concurrent.Callable; import java.util.function.Function; import java.util.stream.Stream; import javax.annotation.Nonnull; +import lombok.SneakyThrows; import org.apache.hc.client5.http.classic.HttpClient; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.io.entity.InputStreamEntity; @@ -40,6 +42,7 @@ import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; @@ -84,7 +87,7 @@ void apiVersion() { verify(exactly(2), postRequestedFor(anyUrl()).withoutQueryParam("api-version")); } - private static Runnable[] chatCompletionCalls() { + private static Runnable[] errorHandlingCalls() { return new Runnable[] { () -> client.chatCompletion(new OpenAiChatCompletionParameters()), () -> @@ -96,7 +99,7 @@ private static Runnable[] chatCompletionCalls() { } @ParameterizedTest - @MethodSource("chatCompletionCalls") + @MethodSource("errorHandlingCalls") void chatCompletionErrorHandling(@Nonnull final Runnable request) { final var errorJson = """ @@ -169,23 +172,33 @@ void chatCompletionErrorHandling(@Nonnull final Runnable request) { softly.assertAll(); } - @Test - void chatCompletion() throws IOException { + private static Callable[] chatCompletionCalls() { + return new Callable[] { + () -> { + final var systemMessage = new OpenAiChatSystemMessage().setContent("You are a helpful AI"); + final var userMessage = + new OpenAiChatUserMessage().addText("Hello World! Why is this phrase so famous?"); + final var request = + new OpenAiChatCompletionParameters().addMessages(systemMessage, userMessage); + return client.chatCompletion(request); + }, + () -> + client + .withSystemPrompt("You are a helpful AI") + .chatCompletion("Hello World! Why is this phrase so famous?") + }; + } + + @SneakyThrows + @ParameterizedTest + @MethodSource("chatCompletionCalls") + void chatCompletion(@Nonnull final Callable request) { try (var inputStream = TEST_FILE_LOADER.apply("chatCompletionResponse.json")) { final String response = new String(inputStream.readAllBytes()); stubFor(post("/chat/completions").willReturn(okJson(response))); - final var systemMessage = - new OpenAiChatMessage.OpenAiChatSystemMessage() - .setContent( - "You are a helpful, friendly and sometimes slightly snarky AI assistant!"); - final var userMessage = - new OpenAiChatUserMessage().addText("Hello World! Why is this phrase so famous?"); - final var request = - new OpenAiChatCompletionParameters().setMessages(List.of(systemMessage, userMessage)); - - final var result = client.chatCompletion(request); + final OpenAiChatCompletionOutput result = request.call(); assertThat(result).isNotNull(); assertThat(result.getCreated()).isEqualTo(1719300073); @@ -252,7 +265,70 @@ void chatCompletion() throws IOException { .withRequestBody( equalToJson( """ - {"messages":[{"role":"system","content":"You are a helpful, friendly and sometimes slightly snarky AI assistant!"},{"role":"user","content":[{"type":"text","text":"Hello World! Why is this phrase so famous?"}]}]}"""))); + { + "messages" : [ { + "role" : "system", + "content" : "You are a helpful AI" + }, { + "role" : "user", + "content" : [ { + "type" : "text", + "text" : "Hello World! Why is this phrase so famous?" + } ] + } ] + }"""))); + } + } + + @Test + @DisplayName("Chat history is not implemented yet") + void history() throws IOException { + try (var inputStream = TEST_FILE_LOADER.apply("chatCompletionResponse.json")) { + + final String response = new String(inputStream.readAllBytes()); + stubFor(post("/chat/completions").willReturn(okJson(response))); + + client.withSystemPrompt("system prompt").chatCompletion("chat completion 1"); + + verify( + exactly(1), + postRequestedFor(urlPathEqualTo("/chat/completions")) + .withRequestBody( + equalToJson( + """ + { + "messages" : [ { + "role" : "system", + "content" : "system prompt" + }, { + "role" : "user", + "content" : [ { + "type" : "text", + "text" : "chat completion 1" + } ] + } ] + }"""))); + + client.withSystemPrompt("system prompt").chatCompletion("chat completion 2"); + + verify( + exactly(1), + postRequestedFor(urlPathEqualTo("/chat/completions")) + .withRequestBody( + equalToJson( + """ + { + "messages" : [ { + "role" : "system", + "content" : "system prompt" + }, { + "role" : "user", + "content" : [ { + "type" : "text", + "text" : "chat completion 2" + } ] + } ] + }"""))); } } @@ -313,11 +389,9 @@ void streamChatCompletionDeltasErrorHandling() throws IOException { final var request = new OpenAiChatCompletionParameters() - .setMessages( - List.of( - new OpenAiChatUserMessage() - .addText( - "Can you give me the first 100 numbers of the Fibonacci sequence?"))); + .addMessages( + new OpenAiChatUserMessage() + .addText("Can you give me the first 100 numbers of the Fibonacci sequence?")); try (Stream stream = client.streamChatCompletionDeltas(request)) { assertThatThrownBy(() -> stream.forEach(System.out::println)) @@ -348,11 +422,9 @@ void streamChatCompletionDeltas() throws IOException { final var request = new OpenAiChatCompletionParameters() - .setMessages( - List.of( - new OpenAiChatUserMessage() - .addText( - "Can you give me the first 100 numbers of the Fibonacci sequence?"))); + .addMessages( + new OpenAiChatUserMessage() + .addText("Can you give me the first 100 numbers of the Fibonacci sequence?")); try (Stream stream = client.streamChatCompletionDeltas(request)) { diff --git a/foundation-models/openai/src/test/java/com/sap/ai/sdk/foundationmodels/openai/model/OpenAiChatCompletionParametersTest.java b/foundation-models/openai/src/test/java/com/sap/ai/sdk/foundationmodels/openai/model/OpenAiChatCompletionParametersTest.java index 7c610f82..416c348b 100644 --- a/foundation-models/openai/src/test/java/com/sap/ai/sdk/foundationmodels/openai/model/OpenAiChatCompletionParametersTest.java +++ b/foundation-models/openai/src/test/java/com/sap/ai/sdk/foundationmodels/openai/model/OpenAiChatCompletionParametersTest.java @@ -131,7 +131,7 @@ public void testCompletionParametersTools() { public void testCompletionParameters() { var params = new OpenAiChatCompletionParameters() - .setMessages(List.of(new OpenAiChatMessage.OpenAiChatUserMessage().addText("foo"))) + .addMessages(new OpenAiChatMessage.OpenAiChatUserMessage().addText("foo")) .setResponseFormat(JSON_OBJECT); // serialization check