diff --git a/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiAssistantMessage.java b/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiAssistantMessage.java index d2edc094e..fd95c3389 100644 --- a/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiAssistantMessage.java +++ b/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiAssistantMessage.java @@ -2,7 +2,6 @@ import static lombok.AccessLevel.PACKAGE; -import com.google.common.annotations.Beta; import com.sap.ai.sdk.foundationmodels.openai.generated.model.ChatCompletionMessageToolCall; import com.sap.ai.sdk.foundationmodels.openai.generated.model.ChatCompletionMessageToolCallFunction; import com.sap.ai.sdk.foundationmodels.openai.generated.model.ChatCompletionRequestAssistantMessage; @@ -25,7 +24,6 @@ * * @since 1.4.0 */ -@Beta @Value @Accessors(fluent = true) @AllArgsConstructor(access = PACKAGE) @@ -39,18 +37,14 @@ public class OpenAiAssistantMessage implements OpenAiMessage { * *

May contain an empty list of {@link OpenAiContentItem} when tool calls are present. */ - @Getter(onMethod_ = @Beta) - @Nonnull - OpenAiMessageContent content; + @Getter @Nonnull OpenAiMessageContent content; /** * The tool calls associated with this message if present. * * @since 1.6.0 */ - @Getter(onMethod_ = @Beta) - @Nonnull - List toolCalls; + @Getter @Nonnull List toolCalls; /** * Creates a new assistant message with the given single message as text content. diff --git a/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiChatCompletionDelta.java b/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiChatCompletionDelta.java index 46b5416e5..2a22c57a8 100644 --- a/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiChatCompletionDelta.java +++ b/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiChatCompletionDelta.java @@ -4,7 +4,6 @@ import static lombok.AccessLevel.PACKAGE; import com.fasterxml.jackson.annotation.JsonCreator; -import com.google.common.annotations.Beta; import com.sap.ai.sdk.core.common.StreamedDelta; import com.sap.ai.sdk.foundationmodels.openai.generated.model.CompletionUsage; import com.sap.ai.sdk.foundationmodels.openai.generated.model.CreateChatCompletionStreamResponse; @@ -22,7 +21,6 @@ * * @since 1.4.0 */ -@Beta @RequiredArgsConstructor(onConstructor_ = @JsonCreator, access = PACKAGE) @Getter @ToString diff --git a/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiChatCompletionRequest.java b/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiChatCompletionRequest.java index 4dfef7f39..b0345b719 100644 --- a/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiChatCompletionRequest.java +++ b/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiChatCompletionRequest.java @@ -1,6 +1,5 @@ package com.sap.ai.sdk.foundationmodels.openai; -import com.google.common.annotations.Beta; import com.google.common.collect.Lists; import com.sap.ai.sdk.foundationmodels.openai.generated.model.ChatCompletionStreamOptions; import com.sap.ai.sdk.foundationmodels.openai.generated.model.ChatCompletionTool; @@ -30,7 +29,6 @@ * API Reference * @since 1.4.0 */ -@Beta @Value @With @AllArgsConstructor(access = AccessLevel.PRIVATE) diff --git a/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiChatCompletionResponse.java b/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiChatCompletionResponse.java index 32131d7b1..ec9fea99e 100644 --- a/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiChatCompletionResponse.java +++ b/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiChatCompletionResponse.java @@ -4,7 +4,6 @@ import static lombok.AccessLevel.NONE; import static lombok.AccessLevel.PACKAGE; -import com.google.common.annotations.Beta; import com.sap.ai.sdk.foundationmodels.openai.generated.model.CompletionUsage; import com.sap.ai.sdk.foundationmodels.openai.generated.model.CreateChatCompletionResponse; import com.sap.ai.sdk.foundationmodels.openai.generated.model.CreateChatCompletionResponseChoicesInner; @@ -21,7 +20,6 @@ * * @since 1.4.0 */ -@Beta @Value @RequiredArgsConstructor(access = PACKAGE) @Setter(value = NONE) 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 7ce79dd16..d87658d4c 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 @@ -101,7 +101,6 @@ public OpenAiClient withApiVersion(@Nonnull final String apiVersion) { * @see AiCoreService#getInferenceDestination(String) * @return a new OpenAI client. */ - @Beta @Nonnull public static OpenAiClient withCustomDestination(@Nonnull final Destination destination) { final OpenAiClient client = new OpenAiClient(destination); @@ -156,7 +155,6 @@ public OpenAiChatCompletionOutput chatCompletion(@Nonnull final String prompt) * @throws OpenAiClientException if the request fails * @since 1.4.0 */ - @Beta @Nonnull public OpenAiChatCompletionResponse chatCompletion( @Nonnull final OpenAiChatCompletionRequest request) throws OpenAiClientException { @@ -173,7 +171,6 @@ public OpenAiChatCompletionResponse chatCompletion( * @throws OpenAiClientException if the request fails * @since 1.4.0 */ - @Beta @Nonnull public CreateChatCompletionResponse chatCompletion( @Nonnull final CreateChatCompletionRequest request) throws OpenAiClientException { @@ -274,7 +271,6 @@ private static void throwOnContentFilter(@Nonnull final OpenAiChatCompletionDelt * @see #streamChatCompletion(String) * @since 1.4.0 */ - @Beta @Nonnull public Stream streamChatCompletionDeltas( @Nonnull final OpenAiChatCompletionRequest request) throws OpenAiClientException { @@ -291,7 +287,6 @@ public Stream streamChatCompletionDeltas( * @see #streamChatCompletionDeltas(OpenAiChatCompletionRequest) for a higher-level API * @since 1.4.0 */ - @Beta @Nonnull public Stream streamChatCompletionDeltas( @Nonnull final CreateChatCompletionRequest request) throws OpenAiClientException { @@ -358,7 +353,6 @@ private void warnIfUnsupportedUsage() { * @see #embedding(EmbeddingsCreateRequest) for full confgurability. * @since 1.4.0 */ - @Beta @Nonnull public OpenAiEmbeddingResponse embedding(@Nonnull final OpenAiEmbeddingRequest request) throws OpenAiClientException { @@ -374,7 +368,6 @@ public OpenAiEmbeddingResponse embedding(@Nonnull final OpenAiEmbeddingRequest r * @see #embedding(OpenAiEmbeddingRequest) for conveninece api * @since 1.4.0 */ - @Beta @Nonnull public EmbeddingsCreate200Response embedding(@Nonnull final EmbeddingsCreateRequest request) throws OpenAiClientException { diff --git a/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiContentItem.java b/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiContentItem.java index 93015ef0b..ed9433415 100644 --- a/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiContentItem.java +++ b/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiContentItem.java @@ -1,11 +1,8 @@ package com.sap.ai.sdk.foundationmodels.openai; -import com.google.common.annotations.Beta; - /** * Represents an item in a {@link OpenAiMessageContent} object. * * @since 1.4.0 */ -@Beta public sealed interface OpenAiContentItem permits OpenAiTextItem, OpenAiImageItem {} diff --git a/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiEmbeddingRequest.java b/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiEmbeddingRequest.java index 83a029164..1f2f84918 100644 --- a/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiEmbeddingRequest.java +++ b/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiEmbeddingRequest.java @@ -1,6 +1,5 @@ package com.sap.ai.sdk.foundationmodels.openai; -import com.google.common.annotations.Beta; import com.sap.ai.sdk.foundationmodels.openai.generated.model.EmbeddingsCreateRequest; import com.sap.ai.sdk.foundationmodels.openai.generated.model.EmbeddingsCreateRequestInput; import java.util.Collections; @@ -16,11 +15,10 @@ * * @since 1.4.0 */ -@Beta @Value public class OpenAiEmbeddingRequest { /** List of tokens to be embedded. */ - @Nonnull private final List tokens; + @Nonnull List tokens; /** * Constructs an OpenAiEmbeddingRequest from a list of strings. diff --git a/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiEmbeddingResponse.java b/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiEmbeddingResponse.java index 82bba0b45..7e05efc35 100644 --- a/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiEmbeddingResponse.java +++ b/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiEmbeddingResponse.java @@ -3,7 +3,6 @@ import static lombok.AccessLevel.NONE; import static lombok.AccessLevel.PACKAGE; -import com.google.common.annotations.Beta; import com.sap.ai.sdk.foundationmodels.openai.generated.model.EmbeddingsCreate200Response; import java.util.ArrayList; import java.util.List; @@ -20,7 +19,6 @@ * * @since 1.4.0 */ -@Beta @Value @AllArgsConstructor(access = PACKAGE) @Setter(value = NONE) diff --git a/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiFunctionCall.java b/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiFunctionCall.java index c3668d26b..af767bfee 100644 --- a/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiFunctionCall.java +++ b/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiFunctionCall.java @@ -1,6 +1,5 @@ package com.sap.ai.sdk.foundationmodels.openai; -import com.google.common.annotations.Beta; import javax.annotation.Nonnull; import lombok.AllArgsConstructor; import lombok.Value; @@ -10,7 +9,6 @@ * * @since 1.6.0 */ -@Beta @Value @AllArgsConstructor(access = lombok.AccessLevel.PACKAGE) public class OpenAiFunctionCall implements OpenAiToolCall { diff --git a/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiImageItem.java b/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiImageItem.java index 7d30a9bdd..787315b62 100644 --- a/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiImageItem.java +++ b/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiImageItem.java @@ -1,6 +1,5 @@ package com.sap.ai.sdk.foundationmodels.openai; -import com.google.common.annotations.Beta; import java.util.Locale; import javax.annotation.Nonnull; @@ -11,7 +10,6 @@ * @param detailLevel the detail level of the image (optional) * @since 1.4.0 */ -@Beta public record OpenAiImageItem(@Nonnull String imageUrl, @Nonnull DetailLevel detailLevel) implements OpenAiContentItem { diff --git a/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiMessage.java b/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiMessage.java index 7a5b9c112..3ffd466cf 100644 --- a/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiMessage.java +++ b/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiMessage.java @@ -1,6 +1,5 @@ package com.sap.ai.sdk.foundationmodels.openai; -import com.google.common.annotations.Beta; import java.util.ArrayList; import java.util.List; import javax.annotation.Nonnull; @@ -10,7 +9,6 @@ * * @since 1.4.0 */ -@Beta public sealed interface OpenAiMessage permits OpenAiUserMessage, OpenAiAssistantMessage, OpenAiSystemMessage, OpenAiToolMessage { @@ -95,7 +93,6 @@ static OpenAiToolMessage tool(@Nonnull final String message, @Nonnull final Stri * * @return the content. */ - @Beta @Nonnull OpenAiMessageContent content(); } diff --git a/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiMessageContent.java b/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiMessageContent.java index e1d49e4b7..f89a94c66 100644 --- a/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiMessageContent.java +++ b/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiMessageContent.java @@ -1,6 +1,5 @@ package com.sap.ai.sdk.foundationmodels.openai; -import com.google.common.annotations.Beta; import java.util.List; import javax.annotation.Nonnull; @@ -10,5 +9,4 @@ * @param items a list of the content items * @since 1.4.0 */ -@Beta public record OpenAiMessageContent(@Nonnull List items) {} diff --git a/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiSystemMessage.java b/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiSystemMessage.java index 9fe3a8f50..df16e13fe 100644 --- a/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiSystemMessage.java +++ b/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiSystemMessage.java @@ -2,7 +2,6 @@ import static lombok.AccessLevel.PACKAGE; -import com.google.common.annotations.Beta; import com.sap.ai.sdk.foundationmodels.openai.generated.model.ChatCompletionRequestMessageContentPartText; import com.sap.ai.sdk.foundationmodels.openai.generated.model.ChatCompletionRequestSystemMessage; import com.sap.ai.sdk.foundationmodels.openai.generated.model.ChatCompletionRequestSystemMessageContent; @@ -20,7 +19,6 @@ * * @since 1.4.0 */ -@Beta @Value @Accessors(fluent = true) @AllArgsConstructor(access = PACKAGE) @@ -30,9 +28,7 @@ public class OpenAiSystemMessage implements OpenAiMessage { @Nonnull String role = "system"; /** The content of the message. */ - @Getter(onMethod_ = @Beta) - @Nonnull - OpenAiMessageContent content; + @Getter @Nonnull OpenAiMessageContent content; /** * Creates a new system message from a string. diff --git a/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiTextItem.java b/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiTextItem.java index 561ba2220..03413b3af 100644 --- a/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiTextItem.java +++ b/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiTextItem.java @@ -1,6 +1,5 @@ package com.sap.ai.sdk.foundationmodels.openai; -import com.google.common.annotations.Beta; import javax.annotation.Nonnull; /** @@ -9,5 +8,4 @@ * @param text the text of the item * @since 1.4.0 */ -@Beta public record OpenAiTextItem(@Nonnull String text) implements OpenAiContentItem {} diff --git a/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiTool.java b/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiTool.java index 8c14199a6..f37751183 100644 --- a/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiTool.java +++ b/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiTool.java @@ -14,7 +14,6 @@ import com.github.victools.jsonschema.generator.SchemaVersion; import com.github.victools.jsonschema.module.jackson.JacksonModule; import com.github.victools.jsonschema.module.jackson.JacksonOption; -import com.google.common.annotations.Beta; import com.sap.ai.sdk.foundationmodels.openai.generated.model.ChatCompletionTool; import com.sap.ai.sdk.foundationmodels.openai.generated.model.FunctionObject; import java.util.ArrayList; @@ -42,7 +41,6 @@ * @since 1.8.0 */ @Slf4j -@Beta @Value @With @Getter(AccessLevel.PACKAGE) @@ -165,7 +163,6 @@ private static SchemaGenerator createSchemaGenerator() { * @param msg the assistant message containing a list of tool calls with arguments * @return The list of tool messages with the results. */ - @Beta @Nonnull static List execute( @Nonnull final List tools, @Nonnull final OpenAiAssistantMessage msg) { diff --git a/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiToolCall.java b/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiToolCall.java index 9a4d3ff27..71fbf3c9f 100644 --- a/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiToolCall.java +++ b/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiToolCall.java @@ -1,6 +1,5 @@ package com.sap.ai.sdk.foundationmodels.openai; -import com.google.common.annotations.Beta; import javax.annotation.Nonnull; /** @@ -8,7 +7,6 @@ * * @since 1.6.0 */ -@Beta public sealed interface OpenAiToolCall permits OpenAiFunctionCall { /** * Creates a new instance of {@link OpenAiToolCall}. diff --git a/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiToolChoice.java b/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiToolChoice.java index 72824219b..0c5aea53e 100644 --- a/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiToolChoice.java +++ b/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiToolChoice.java @@ -1,6 +1,5 @@ package com.sap.ai.sdk.foundationmodels.openai; -import com.google.common.annotations.Beta; import com.sap.ai.sdk.foundationmodels.openai.generated.model.ChatCompletionNamedToolChoice; import com.sap.ai.sdk.foundationmodels.openai.generated.model.ChatCompletionNamedToolChoiceFunction; import com.sap.ai.sdk.foundationmodels.openai.generated.model.ChatCompletionToolChoiceOption; @@ -14,7 +13,6 @@ * * @since 1.4.0 */ -@Beta @EqualsAndHashCode @RequiredArgsConstructor(access = AccessLevel.PROTECTED) public class OpenAiToolChoice { diff --git a/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiToolMessage.java b/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiToolMessage.java index 48dca596e..bd859ec2f 100644 --- a/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiToolMessage.java +++ b/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiToolMessage.java @@ -2,7 +2,6 @@ import static lombok.AccessLevel.PACKAGE; -import com.google.common.annotations.Beta; import com.sap.ai.sdk.foundationmodels.openai.generated.model.ChatCompletionRequestToolMessage; import com.sap.ai.sdk.foundationmodels.openai.generated.model.ChatCompletionRequestToolMessageContent; import java.util.List; @@ -16,7 +15,6 @@ * * @since 1.4.0 */ -@Beta @Value @Accessors(fluent = true) @AllArgsConstructor(access = PACKAGE) @@ -29,7 +27,7 @@ public class OpenAiToolMessage implements OpenAiMessage { @Nonnull OpenAiMessageContent content; /** The tool call id associated with this message. */ - @Nonnull private final String toolCallId; + @Nonnull String toolCallId; /** * Creates a new tool message from a tool execution response and tool call id. diff --git a/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiUserMessage.java b/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiUserMessage.java index 4f8cc4bf6..0549297b3 100644 --- a/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiUserMessage.java +++ b/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiUserMessage.java @@ -2,7 +2,6 @@ import static lombok.AccessLevel.PACKAGE; -import com.google.common.annotations.Beta; import com.sap.ai.sdk.foundationmodels.openai.generated.model.ChatCompletionRequestMessageContentPartImage; import com.sap.ai.sdk.foundationmodels.openai.generated.model.ChatCompletionRequestMessageContentPartImageImageUrl; import com.sap.ai.sdk.foundationmodels.openai.generated.model.ChatCompletionRequestMessageContentPartText; @@ -25,7 +24,6 @@ * * @since 1.4.0 */ -@Beta @Value @Accessors(fluent = true) @AllArgsConstructor(access = PACKAGE) @@ -35,9 +33,7 @@ public class OpenAiUserMessage implements OpenAiMessage { @Nonnull String role = "user"; /** The content of the message. */ - @Getter(onMethod_ = @Beta) - @Nonnull - OpenAiMessageContent content; + @Getter @Nonnull OpenAiMessageContent content; /** * Creates a new user message from a string. diff --git a/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiUtils.java b/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiUtils.java index 0b12565b9..18d9b6e06 100644 --- a/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiUtils.java +++ b/foundation-models/openai/src/main/java/com/sap/ai/sdk/foundationmodels/openai/OpenAiUtils.java @@ -3,7 +3,6 @@ import static com.sap.ai.sdk.core.JacksonConfiguration.getDefaultObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.annotations.Beta; import com.sap.ai.sdk.foundationmodels.openai.generated.model.ChatCompletionRequestMessage; import com.sap.ai.sdk.foundationmodels.openai.generated.model.ChatCompletionsCreate200Response; import javax.annotation.Nonnull; @@ -15,7 +14,6 @@ * * @since 1.4.0 */ -@Beta class OpenAiUtils { /** diff --git a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/controllers/OpenAiController.java b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/controllers/OpenAiController.java index 76a3415a1..60dc45583 100644 --- a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/controllers/OpenAiController.java +++ b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/controllers/OpenAiController.java @@ -4,7 +4,7 @@ import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import com.sap.ai.sdk.app.services.OpenAiService; -import com.sap.ai.sdk.foundationmodels.openai.model.OpenAiUsage; +import com.sap.ai.sdk.foundationmodels.openai.generated.model.CompletionUsage; import com.sap.cloud.sdk.cloudplatform.thread.ThreadContextExecutors; import java.io.IOException; import java.util.concurrent.atomic.AtomicReference; @@ -47,14 +47,14 @@ ResponseEntity streamChatCompletionDeltas() { final var message = "Can you give me the first 100 numbers of the Fibonacci sequence?"; final var stream = service.streamChatCompletionDeltas(message); final var emitter = new ResponseBodyEmitter(); - final var totalUsage = new AtomicReference(); + final var totalUsage = new AtomicReference(); final Runnable consumeStream = () -> { try (stream) { stream.forEach( delta -> { // Instead of getCompletionUsage(MAPPER), we now use getUsage() - final var usage = delta.getUsage(); + final var usage = delta.getCompletionUsage(); totalUsage.compareAndExchange(null, usage); send(emitter, delta.getDeltaContent()); }); @@ -116,7 +116,7 @@ Object chatCompletionImage( if ("json".equals(format)) { return response; } - return response.getChoices().get(0).getMessage(); + return response.getContent(); } @GetMapping("/chatCompletionToolExecution") diff --git a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/OpenAiService.java b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/OpenAiService.java index 13400d059..957099c46 100644 --- a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/OpenAiService.java +++ b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/OpenAiService.java @@ -3,38 +3,29 @@ import static com.sap.ai.sdk.foundationmodels.openai.OpenAiModel.GPT_4O; import static com.sap.ai.sdk.foundationmodels.openai.OpenAiModel.GPT_4O_MINI; import static com.sap.ai.sdk.foundationmodels.openai.OpenAiModel.TEXT_EMBEDDING_3_SMALL; -import static com.sap.ai.sdk.foundationmodels.openai.model.OpenAiChatCompletionTool.ToolType.FUNCTION; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.module.jsonSchema.JsonSchemaGenerator; import com.sap.ai.sdk.core.AiCoreService; +import com.sap.ai.sdk.foundationmodels.openai.OpenAiChatCompletionDelta; +import com.sap.ai.sdk.foundationmodels.openai.OpenAiChatCompletionRequest; +import com.sap.ai.sdk.foundationmodels.openai.OpenAiChatCompletionResponse; import com.sap.ai.sdk.foundationmodels.openai.OpenAiClient; -import com.sap.ai.sdk.foundationmodels.openai.model.OpenAiChatCompletionDelta; -import com.sap.ai.sdk.foundationmodels.openai.model.OpenAiChatCompletionFunction; -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.OpenAiChatCompletionTool; -import com.sap.ai.sdk.foundationmodels.openai.model.OpenAiChatMessage; -import com.sap.ai.sdk.foundationmodels.openai.model.OpenAiChatToolCall; -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.OpenAiEmbeddingRequest; +import com.sap.ai.sdk.foundationmodels.openai.OpenAiEmbeddingResponse; +import com.sap.ai.sdk.foundationmodels.openai.OpenAiImageItem; +import com.sap.ai.sdk.foundationmodels.openai.OpenAiMessage; +import com.sap.ai.sdk.foundationmodels.openai.OpenAiTool; import java.util.ArrayList; import java.util.List; -import java.util.Map; import java.util.stream.Stream; import javax.annotation.Nonnull; import lombok.extern.slf4j.Slf4j; +import lombok.val; import org.springframework.stereotype.Service; -/** Service class for OpenAI service */ +/** Service class for OpenAI service using latest convenience api */ @Service @Slf4j public class OpenAiService { - private static final ObjectMapper JACKSON = new ObjectMapper(); - /** * Chat request to OpenAI * @@ -42,8 +33,31 @@ public class OpenAiService { * @return the assistant message response */ @Nonnull - public OpenAiChatCompletionOutput chatCompletion(@Nonnull final String prompt) { - return OpenAiClient.forModel(GPT_4O_MINI).chatCompletion(prompt); + public OpenAiChatCompletionResponse chatCompletion(@Nonnull final String prompt) { + return OpenAiClient.forModel(GPT_4O_MINI) + .chatCompletion(new OpenAiChatCompletionRequest(prompt)); + } + + /** + * Chat requests to OpenAI and updating the messages history + * + * @param previousMessage The request to send to the assistant + * @return the assistant message response + */ + @Nonnull + public OpenAiChatCompletionResponse messagesHistory(@Nonnull final String previousMessage) { + val messagesList = new ArrayList(); + messagesList.add(OpenAiMessage.user(previousMessage)); + + final OpenAiChatCompletionResponse result = + OpenAiClient.forModel(GPT_4O_MINI) + .chatCompletion(new OpenAiChatCompletionRequest(messagesList)); + + messagesList.add(result.getMessage()); + messagesList.add(OpenAiMessage.user("What is the typical food there?")); + + return OpenAiClient.forModel(GPT_4O_MINI) + .chatCompletion(new OpenAiChatCompletionRequest(messagesList)); } /** @@ -55,9 +69,7 @@ public OpenAiChatCompletionOutput chatCompletion(@Nonnull final String prompt) { @Nonnull public Stream streamChatCompletionDeltas( @Nonnull final String message) { - final var request = - new OpenAiChatCompletionParameters() - .addMessages(new OpenAiChatMessage.OpenAiChatUserMessage().addText(message)); + final var request = new OpenAiChatCompletionRequest(OpenAiMessage.user(message)); return OpenAiClient.forModel(GPT_4O_MINI).streamChatCompletionDeltas(request); } @@ -82,93 +94,50 @@ public Stream streamChatCompletion(@Nonnull final String message) { * @return the assistant message response */ @Nonnull - public OpenAiChatCompletionOutput chatCompletionImage(@Nonnull final String linkToImage) { - final var request = - new OpenAiChatCompletionParameters() - .addMessages( - new OpenAiChatMessage.OpenAiChatUserMessage() - .addText("Describe the following image.") - .addImage( - linkToImage, - OpenAiChatMessage.OpenAiChatUserMessage.ImageDetailLevel.HIGH)); - - return OpenAiClient.forModel(GPT_4O).chatCompletion(request); + public OpenAiChatCompletionResponse chatCompletionImage(@Nonnull final String linkToImage) { + + final var userMessage = + OpenAiMessage.user("Describe the following image.") + .withImage(linkToImage, OpenAiImageItem.DetailLevel.HIGH); + + return OpenAiClient.forModel(GPT_4O) + .chatCompletion(new OpenAiChatCompletionRequest(userMessage)); } /** - * Executes a chat completion request to OpenAI with a tool that calculates the weather. + * Chat request to OpenAI with tool that gets the weather for a given location and unit. The tool + * executed and the result is sent back to the assistant. * * @param location The location to get the weather for. * @param unit The unit of temperature to use. * @return The assistant message response. */ @Nonnull - public OpenAiChatCompletionOutput chatCompletionToolExecution( + public OpenAiChatCompletionResponse chatCompletionToolExecution( @Nonnull final String location, @Nonnull final String unit) { + final OpenAiClient client = OpenAiClient.forModel(GPT_4O_MINI); - // 1. Define the function - final Map schemaMap = generateSchema(WeatherMethod.Request.class); - final var function = - new OpenAiChatCompletionFunction() - .setName("weather") - .setDescription("Get the weather for the given location") - .setParameters(schemaMap); - final var tool = new OpenAiChatCompletionTool().setType(FUNCTION).setFunction(function); - - final var messages = new ArrayList(); - messages.add( - new OpenAiChatMessage.OpenAiChatUserMessage() - .addText("What's the weather in %s in %s?".formatted(location, unit))); - - // Assistant will call the function - final var request = - new OpenAiChatCompletionParameters() - .addMessages(messages.toArray(OpenAiChatMessage[]::new)) - .setTools(List.of(tool)); - - final OpenAiChatCompletionOutput response = - OpenAiClient.forModel(GPT_4O_MINI).chatCompletion(request); - - // 2. Optionally, execute the function. - final OpenAiChatToolCall toolCall = - response.getChoices().get(0).getMessage().getToolCalls().get(0); - final WeatherMethod.Request arguments = - parseJson(toolCall.getFunction().getArguments(), WeatherMethod.Request.class); - final WeatherMethod.Response currentWeather = WeatherMethod.getCurrentWeather(arguments); - - final OpenAiChatMessage.OpenAiChatAssistantMessage assistantMessage = - response.getChoices().get(0).getMessage(); - messages.add(assistantMessage); - - final var toolMessage = - new OpenAiChatMessage.OpenAiChatToolMessage() - .setToolCallId(toolCall.getId()) - .setContent(currentWeather.toString()); - messages.add(toolMessage); - - final var finalRequest = - new OpenAiChatCompletionParameters() - .addMessages(messages.toArray(OpenAiChatMessage[]::new)); - - return OpenAiClient.forModel(GPT_4O_MINI).chatCompletion(finalRequest); - } + final var messages = new ArrayList(); + messages.add(OpenAiMessage.user("What's the weather in %s in %s?".formatted(location, unit))); - private static T parseJson(@Nonnull final String rawJson, @Nonnull final Class clazz) { - try { - return JACKSON.readValue(rawJson, clazz); - } catch (JsonProcessingException e) { - throw new IllegalArgumentException("Failed to parse tool call arguments: " + rawJson, e); - } - } - - private static Map generateSchema(@Nonnull final Class clazz) { - final var jsonSchemaGenerator = new JsonSchemaGenerator(JACKSON); - try { - final var schema = jsonSchemaGenerator.generateSchema(clazz); - return JACKSON.convertValue(schema, new TypeReference<>() {}); - } catch (JsonMappingException e) { - throw new IllegalArgumentException("Could not generate schema for " + clazz.getName(), e); - } + // 1. Define the function + final List tools = + List.of( + OpenAiTool.forFunction(WeatherMethod::getCurrentWeather) + .withArgument(WeatherMethod.Request.class) + .withName("weather") + .withDescription("Get the weather for the given location")); + + // 2. Assistant calls the function + final var request = new OpenAiChatCompletionRequest(messages).withToolsExecutable(tools); + final OpenAiChatCompletionResponse response = client.chatCompletion(request); + + // 3. Execute the tool calls + messages.add(response.getMessage()); + messages.addAll(response.executeTools()); + + // 4. Have model run the final request with incorporated tool results + return client.chatCompletion(request.withMessages(messages)); } /** @@ -178,8 +147,8 @@ private static Map generateSchema(@Nonnull final Class clazz) * @return the embedding response */ @Nonnull - public OpenAiEmbeddingOutput embedding(@Nonnull final String input) { - final var request = new OpenAiEmbeddingParameters().setInput(input); + public OpenAiEmbeddingResponse embedding(@Nonnull final String input) { + final var request = new OpenAiEmbeddingRequest(List.of(input)); return OpenAiClient.forModel(TEXT_EMBEDDING_3_SMALL).embedding(request); } @@ -192,12 +161,13 @@ public OpenAiEmbeddingOutput embedding(@Nonnull final String input) { * @return the assistant message response */ @Nonnull - public OpenAiChatCompletionOutput chatCompletionWithResource( + public OpenAiChatCompletionResponse chatCompletionWithResource( @Nonnull final String resourceGroup, @Nonnull final String prompt) { final var destination = new AiCoreService().getInferenceDestination(resourceGroup).forModel(GPT_4O); - return OpenAiClient.withCustomDestination(destination).chatCompletion(prompt); + return OpenAiClient.withCustomDestination(destination) + .chatCompletion(new OpenAiChatCompletionRequest(prompt)); } } diff --git a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/OpenAiServiceDeprecated.java b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/OpenAiServiceDeprecated.java new file mode 100644 index 000000000..60c42c4ce --- /dev/null +++ b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/OpenAiServiceDeprecated.java @@ -0,0 +1,204 @@ +package com.sap.ai.sdk.app.services; + +import static com.sap.ai.sdk.foundationmodels.openai.OpenAiModel.GPT_4O; +import static com.sap.ai.sdk.foundationmodels.openai.OpenAiModel.GPT_4O_MINI; +import static com.sap.ai.sdk.foundationmodels.openai.OpenAiModel.TEXT_EMBEDDING_3_SMALL; +import static com.sap.ai.sdk.foundationmodels.openai.model.OpenAiChatCompletionTool.ToolType.FUNCTION; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.module.jsonSchema.JsonSchemaGenerator; +import com.sap.ai.sdk.core.AiCoreService; +import com.sap.ai.sdk.foundationmodels.openai.OpenAiClient; +import com.sap.ai.sdk.foundationmodels.openai.model.OpenAiChatCompletionDelta; +import com.sap.ai.sdk.foundationmodels.openai.model.OpenAiChatCompletionFunction; +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.OpenAiChatCompletionTool; +import com.sap.ai.sdk.foundationmodels.openai.model.OpenAiChatMessage; +import com.sap.ai.sdk.foundationmodels.openai.model.OpenAiChatToolCall; +import com.sap.ai.sdk.foundationmodels.openai.model.OpenAiEmbeddingOutput; +import com.sap.ai.sdk.foundationmodels.openai.model.OpenAiEmbeddingParameters; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; +import javax.annotation.Nonnull; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +/** Service class for OpenAI service */ +@Service +@Slf4j +@Deprecated +public class OpenAiServiceDeprecated { + private static final ObjectMapper JACKSON = new ObjectMapper(); + + /** + * Chat request to OpenAI + * + * @param prompt The prompt to send to the assistant + * @return the assistant message response + */ + @Nonnull + public OpenAiChatCompletionOutput chatCompletion(@Nonnull final String prompt) { + return OpenAiClient.forModel(GPT_4O_MINI).chatCompletion(prompt); + } + + /** + * Asynchronous stream of an OpenAI chat request + * + * @param message The message to send to the assistant + * @return the emitter that streams the assistant message response + */ + @Nonnull + public Stream streamChatCompletionDeltas( + @Nonnull final String message) { + final var request = + new OpenAiChatCompletionParameters() + .addMessages(new OpenAiChatMessage.OpenAiChatUserMessage().addText(message)); + + return OpenAiClient.forModel(GPT_4O_MINI).streamChatCompletionDeltas(request); + } + + /** + * Asynchronous stream of an OpenAI chat request + * + * @param message The message to send to the assistant + * @return the emitter that streams the assistant message response + */ + @Nonnull + public Stream streamChatCompletion(@Nonnull final String message) { + return OpenAiClient.forModel(GPT_4O_MINI) + .withSystemPrompt("Be a good, honest AI and answer the following question:") + .streamChatCompletion(message); + } + + /** + * Chat request to OpenAI with an image + * + * @param linkToImage The link to the image + * @return the assistant message response + */ + @Nonnull + public OpenAiChatCompletionOutput chatCompletionImage(@Nonnull final String linkToImage) { + final var request = + new OpenAiChatCompletionParameters() + .addMessages( + new OpenAiChatMessage.OpenAiChatUserMessage() + .addText("Describe the following image.") + .addImage( + linkToImage, + OpenAiChatMessage.OpenAiChatUserMessage.ImageDetailLevel.HIGH)); + + return OpenAiClient.forModel(GPT_4O).chatCompletion(request); + } + + /** + * Executes a chat completion request to OpenAI with a tool that calculates the weather. + * + * @param location The location to get the weather for. + * @param unit The unit of temperature to use. + * @return The assistant message response. + */ + @Nonnull + public OpenAiChatCompletionOutput chatCompletionToolExecution( + @Nonnull final String location, @Nonnull final String unit) { + + // 1. Define the function + final Map schemaMap = generateSchema(WeatherMethod.Request.class); + final var function = + new OpenAiChatCompletionFunction() + .setName("weather") + .setDescription("Get the weather for the given location") + .setParameters(schemaMap); + final var tool = new OpenAiChatCompletionTool().setType(FUNCTION).setFunction(function); + + final var messages = new ArrayList(); + messages.add( + new OpenAiChatMessage.OpenAiChatUserMessage() + .addText("What's the weather in %s in %s?".formatted(location, unit))); + + // Assistant will call the function + final var request = + new OpenAiChatCompletionParameters() + .addMessages(messages.toArray(OpenAiChatMessage[]::new)) + .setTools(List.of(tool)); + + final OpenAiChatCompletionOutput response = + OpenAiClient.forModel(GPT_4O_MINI).chatCompletion(request); + + // 2. Optionally, execute the function. + final OpenAiChatToolCall toolCall = + response.getChoices().get(0).getMessage().getToolCalls().get(0); + final WeatherMethod.Request arguments = + parseJson(toolCall.getFunction().getArguments(), WeatherMethod.Request.class); + final WeatherMethod.Response currentWeather = WeatherMethod.getCurrentWeather(arguments); + + final OpenAiChatMessage.OpenAiChatAssistantMessage assistantMessage = + response.getChoices().get(0).getMessage(); + messages.add(assistantMessage); + + final var toolMessage = + new OpenAiChatMessage.OpenAiChatToolMessage() + .setToolCallId(toolCall.getId()) + .setContent(currentWeather.toString()); + messages.add(toolMessage); + + final var finalRequest = + new OpenAiChatCompletionParameters() + .addMessages(messages.toArray(OpenAiChatMessage[]::new)); + + return OpenAiClient.forModel(GPT_4O_MINI).chatCompletion(finalRequest); + } + + private static T parseJson(@Nonnull final String rawJson, @Nonnull final Class clazz) { + try { + return JACKSON.readValue(rawJson, clazz); + } catch (JsonProcessingException e) { + throw new IllegalArgumentException("Failed to parse tool call arguments: " + rawJson, e); + } + } + + private static Map generateSchema(@Nonnull final Class clazz) { + final var jsonSchemaGenerator = new JsonSchemaGenerator(JACKSON); + try { + final var schema = jsonSchemaGenerator.generateSchema(clazz); + return JACKSON.convertValue(schema, new TypeReference<>() {}); + } catch (JsonMappingException e) { + throw new IllegalArgumentException("Could not generate schema for " + clazz.getName(), e); + } + } + + /** + * Get the embedding of a text + * + * @param input The text to embed + * @return the embedding response + */ + @Nonnull + public OpenAiEmbeddingOutput embedding(@Nonnull final String input) { + final var request = new OpenAiEmbeddingParameters().setInput(input); + + return OpenAiClient.forModel(TEXT_EMBEDDING_3_SMALL).embedding(request); + } + + /** + * Chat request to OpenAI filtering by resource group + * + * @param resourceGroup The resource group to use + * @param prompt The prompt to send to the assistant + * @return the assistant message response + */ + @Nonnull + public OpenAiChatCompletionOutput chatCompletionWithResource( + @Nonnull final String resourceGroup, @Nonnull final String prompt) { + + final var destination = + new AiCoreService().getInferenceDestination(resourceGroup).forModel(GPT_4O); + + return OpenAiClient.withCustomDestination(destination).chatCompletion(prompt); + } +} diff --git a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/OpenAiServiceV2.java b/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/OpenAiServiceV2.java deleted file mode 100644 index b0b3acbd9..000000000 --- a/sample-code/spring-app/src/main/java/com/sap/ai/sdk/app/services/OpenAiServiceV2.java +++ /dev/null @@ -1,173 +0,0 @@ -package com.sap.ai.sdk.app.services; - -import static com.sap.ai.sdk.foundationmodels.openai.OpenAiModel.GPT_4O; -import static com.sap.ai.sdk.foundationmodels.openai.OpenAiModel.GPT_4O_MINI; -import static com.sap.ai.sdk.foundationmodels.openai.OpenAiModel.TEXT_EMBEDDING_3_SMALL; - -import com.sap.ai.sdk.core.AiCoreService; -import com.sap.ai.sdk.foundationmodels.openai.OpenAiChatCompletionDelta; -import com.sap.ai.sdk.foundationmodels.openai.OpenAiChatCompletionRequest; -import com.sap.ai.sdk.foundationmodels.openai.OpenAiChatCompletionResponse; -import com.sap.ai.sdk.foundationmodels.openai.OpenAiClient; -import com.sap.ai.sdk.foundationmodels.openai.OpenAiEmbeddingRequest; -import com.sap.ai.sdk.foundationmodels.openai.OpenAiEmbeddingResponse; -import com.sap.ai.sdk.foundationmodels.openai.OpenAiImageItem; -import com.sap.ai.sdk.foundationmodels.openai.OpenAiMessage; -import com.sap.ai.sdk.foundationmodels.openai.OpenAiTool; -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Stream; -import javax.annotation.Nonnull; -import lombok.extern.slf4j.Slf4j; -import lombok.val; -import org.springframework.stereotype.Service; - -/** Service class for OpenAI service using latest convenience api */ -@Service -@Slf4j -public class OpenAiServiceV2 { - /** - * Chat request to OpenAI - * - * @param prompt The prompt to send to the assistant - * @return the assistant message response - */ - @Nonnull - public OpenAiChatCompletionResponse chatCompletion(@Nonnull final String prompt) { - return OpenAiClient.forModel(GPT_4O_MINI) - .chatCompletion(new OpenAiChatCompletionRequest(prompt)); - } - - /** - * Chat requests to OpenAI and updating the messages history - * - * @param previousMessage The request to send to the assistant - * @return the assistant message response - */ - @Nonnull - public OpenAiChatCompletionResponse messagesHistory(@Nonnull final String previousMessage) { - val messagesList = new ArrayList(); - messagesList.add(OpenAiMessage.user(previousMessage)); - - final OpenAiChatCompletionResponse result = - OpenAiClient.forModel(GPT_4O_MINI) - .chatCompletion(new OpenAiChatCompletionRequest(messagesList)); - - messagesList.add(result.getMessage()); - messagesList.add(OpenAiMessage.user("What is the typical food there?")); - - return OpenAiClient.forModel(GPT_4O_MINI) - .chatCompletion(new OpenAiChatCompletionRequest(messagesList)); - } - - /** - * Asynchronous stream of an OpenAI chat request - * - * @param message The message to send to the assistant - * @return the emitter that streams the assistant message response - */ - @Nonnull - public Stream streamChatCompletionDeltas( - @Nonnull final String message) { - final var request = new OpenAiChatCompletionRequest(OpenAiMessage.user(message)); - - return OpenAiClient.forModel(GPT_4O_MINI).streamChatCompletionDeltas(request); - } - - /** - * Asynchronous stream of an OpenAI chat request - * - * @param message The message to send to the assistant - * @return the emitter that streams the assistant message response - */ - @Nonnull - public Stream streamChatCompletion(@Nonnull final String message) { - return OpenAiClient.forModel(GPT_4O_MINI) - .withSystemPrompt("Be a good, honest AI and answer the following question:") - .streamChatCompletion(message); - } - - /** - * Chat request to OpenAI with an image - * - * @param linkToImage The link to the image - * @return the assistant message response - */ - @Nonnull - public OpenAiChatCompletionResponse chatCompletionImage(@Nonnull final String linkToImage) { - - final var userMessage = - OpenAiMessage.user("Describe the following image.") - .withImage(linkToImage, OpenAiImageItem.DetailLevel.HIGH); - - return OpenAiClient.forModel(GPT_4O) - .chatCompletion(new OpenAiChatCompletionRequest(userMessage)); - } - - /** - * Chat request to OpenAI with tool that gets the weather for a given location and unit. The tool - * executed and the result is sent back to the assistant. - * - * @param location The location to get the weather for. - * @param unit The unit of temperature to use. - * @return The assistant message response. - */ - @Nonnull - public OpenAiChatCompletionResponse chatCompletionToolExecution( - @Nonnull final String location, @Nonnull final String unit) { - final OpenAiClient client = OpenAiClient.forModel(GPT_4O_MINI); - - final var messages = new ArrayList(); - messages.add(OpenAiMessage.user("What's the weather in %s in %s?".formatted(location, unit))); - - // 1. Define the function - final List tools = - List.of( - OpenAiTool.forFunction(WeatherMethod::getCurrentWeather) - .withArgument(WeatherMethod.Request.class) - .withName("weather") - .withDescription("Get the weather for the given location")); - - // 2. Assistant calls the function - final var request = new OpenAiChatCompletionRequest(messages).withToolsExecutable(tools); - final OpenAiChatCompletionResponse response = client.chatCompletion(request); - - // 3. Execute the tool calls - messages.add(response.getMessage()); - messages.addAll(response.executeTools()); - - // 4. Have model run the final request with incorporated tool results - return client.chatCompletion(request.withMessages(messages)); - } - - /** - * Get the embedding of a text - * - * @param input The text to embed - * @return the embedding response - */ - @Nonnull - public OpenAiEmbeddingResponse embedding(@Nonnull final String input) { - final var request = new OpenAiEmbeddingRequest(List.of(input)); - - return OpenAiClient.forModel(TEXT_EMBEDDING_3_SMALL).embedding(request); - } - - /** - * Chat request to OpenAI filtering by resource group - * - * @param resourceGroup The resource group to use - * @param prompt The prompt to send to the assistant - * @return the assistant message response - */ - @Nonnull - public OpenAiChatCompletionResponse chatCompletionWithResource( - @Nonnull final String resourceGroup, @Nonnull final String prompt) { - - final var destination = - new AiCoreService().getInferenceDestination(resourceGroup).forModel(GPT_4O); - - return OpenAiClient.withCustomDestination(destination) - .chatCompletion(new OpenAiChatCompletionRequest(prompt)); - } -} diff --git a/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/OpenAiDeprecatedTest.java b/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/OpenAiDeprecatedTest.java new file mode 100644 index 000000000..afc76461d --- /dev/null +++ b/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/OpenAiDeprecatedTest.java @@ -0,0 +1,103 @@ +package com.sap.ai.sdk.app.controllers; + +import static com.sap.ai.sdk.foundationmodels.openai.OpenAiModel.GPT_4O_MINI; +import static org.assertj.core.api.Assertions.assertThat; + +import com.sap.ai.sdk.app.services.OpenAiServiceDeprecated; +import com.sap.ai.sdk.foundationmodels.openai.OpenAiClient; +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.concurrent.atomic.AtomicInteger; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +@Slf4j +class OpenAiDeprecatedTest { + OpenAiServiceDeprecated service; + + @BeforeEach + void setUp() { + service = new OpenAiServiceDeprecated(); + } + + @Test + void chatCompletion() { + final var completion = service.chatCompletion("Who is the prettiest"); + + final var message = completion.getChoices().get(0).getMessage(); + assertThat(message.getRole()).isEqualTo("assistant"); + assertThat(message.getContent()).isNotEmpty(); + } + + @Test + void chatCompletionImage() { + final var completion = + service.chatCompletionImage( + "https://upload.wikimedia.org/wikipedia/commons/thumb/5/59/SAP_2011_logo.svg/440px-SAP_2011_logo.svg.png"); + + final var message = completion.getChoices().get(0).getMessage(); + assertThat(message.getRole()).isEqualTo("assistant"); + assertThat(message.getContent()).isNotEmpty(); + } + + @Test + void streamChatCompletion() { + final var request = + new OpenAiChatCompletionParameters() + .addMessages(new OpenAiChatUserMessage().addText("Who is the prettiest?")); + + final var totalOutput = new OpenAiChatCompletionOutput(); + final var filledDeltaCount = new AtomicInteger(0); + OpenAiClient.forModel(GPT_4O_MINI) + .streamChatCompletionDeltas(request) + .peek(totalOutput::addDelta) + // foreach consumes all elements, closing the stream at the end + .forEach( + delta -> { + final String deltaContent = delta.getDeltaContent(); + log.info("delta: {}", delta); + if (!deltaContent.isEmpty()) { + filledDeltaCount.incrementAndGet(); + } + }); + + // the first two and the last delta don't have any content + // see OpenAiChatCompletionDelta#getDeltaContent + assertThat(filledDeltaCount.get()).isGreaterThan(0); + + assertThat(totalOutput.getChoices()).isNotEmpty(); + assertThat(totalOutput.getChoices().get(0).getMessage().getContent()).isNotEmpty(); + assertThat(totalOutput.getPromptFilterResults()).isNotNull(); + assertThat(totalOutput.getChoices().get(0).getContentFilterResults()).isNotNull(); + } + + @Test + void embedding() { + final var embedding = service.embedding("Hello world"); + + assertThat(embedding.getData().get(0).getEmbedding()).hasSizeGreaterThan(1); + assertThat(embedding.getModel()).isEqualTo("text-embedding-3-small"); + assertThat(embedding.getObject()).isEqualTo("list"); + } + + @Test + void chatCompletionWithResource() { + final var completion = + service.chatCompletionWithResource("ai-sdk-java-e2e", "Where is the nearest coffee shop?"); + + final var message = completion.getChoices().get(0).getMessage(); + assertThat(message.getRole()).isEqualTo("assistant"); + assertThat(message.getContent()).isNotEmpty(); + } + + @Test + void chatCompletionToolExecution() { + final var completion = service.chatCompletionToolExecution("Dubai", "°C"); + + String content = completion.getContent(); + + assertThat(content).contains("°C"); + } +} diff --git a/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/OpenAiTest.java b/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/OpenAiTest.java index 7a029ebdd..6a541d489 100644 --- a/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/OpenAiTest.java +++ b/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/OpenAiTest.java @@ -1,14 +1,17 @@ package com.sap.ai.sdk.app.controllers; import static com.sap.ai.sdk.foundationmodels.openai.OpenAiModel.GPT_4O_MINI; +import static com.sap.ai.sdk.foundationmodels.openai.generated.model.ChatCompletionResponseMessageRole.ASSISTANT; import static org.assertj.core.api.Assertions.assertThat; import com.sap.ai.sdk.app.services.OpenAiService; +import com.sap.ai.sdk.foundationmodels.openai.OpenAiChatCompletionRequest; import com.sap.ai.sdk.foundationmodels.openai.OpenAiClient; -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 com.sap.ai.sdk.foundationmodels.openai.OpenAiMessage; +import com.sap.ai.sdk.foundationmodels.openai.generated.model.CompletionUsage; +import java.util.ArrayList; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -26,9 +29,13 @@ void setUp() { void chatCompletion() { final var completion = service.chatCompletion("Who is the prettiest"); - final var message = completion.getChoices().get(0).getMessage(); - assertThat(message.getRole()).isEqualTo("assistant"); - assertThat(message.getContent()).isNotEmpty(); + assertThat(completion.getChoice().getMessage().getRole()).isEqualTo(ASSISTANT); + assertThat(completion.getContent()).isNotEmpty(); + } + + @Test + void testMessagesHistory() { + assertThat(service.messagesHistory("What is the capital of France?").getContent()).isNotEmpty(); } @Test @@ -37,25 +44,24 @@ void chatCompletionImage() { service.chatCompletionImage( "https://upload.wikimedia.org/wikipedia/commons/thumb/5/59/SAP_2011_logo.svg/440px-SAP_2011_logo.svg.png"); - final var message = completion.getChoices().get(0).getMessage(); - assertThat(message.getRole()).isEqualTo("assistant"); - assertThat(message.getContent()).isNotEmpty(); + assertThat(completion.getContent()).isNotEmpty(); + assertThat(completion.getChoice().getMessage().getRole()).isEqualTo(ASSISTANT); } @Test void streamChatCompletion() { - final var request = - new OpenAiChatCompletionParameters() - .addMessages(new OpenAiChatUserMessage().addText("Who is the prettiest?")); + final var userMessage = OpenAiMessage.user("Who is the prettiest?"); + final var prompt = new OpenAiChatCompletionRequest(userMessage); - final var totalOutput = new OpenAiChatCompletionOutput(); + final var usageRef = new AtomicReference(); final var filledDeltaCount = new AtomicInteger(0); + OpenAiClient.forModel(GPT_4O_MINI) - .streamChatCompletionDeltas(request) - .peek(totalOutput::addDelta) + .streamChatCompletionDeltas(prompt) // foreach consumes all elements, closing the stream at the end .forEach( delta -> { + usageRef.compareAndExchange(null, delta.getCompletionUsage()); final String deltaContent = delta.getDeltaContent(); log.info("delta: {}", delta); if (!deltaContent.isEmpty()) { @@ -63,23 +69,24 @@ void streamChatCompletion() { } }); - // the first two and the last delta don't have any content - // see OpenAiChatCompletionDelta#getDeltaContent assertThat(filledDeltaCount.get()).isGreaterThan(0); - assertThat(totalOutput.getChoices()).isNotEmpty(); - assertThat(totalOutput.getChoices().get(0).getMessage().getContent()).isNotEmpty(); - assertThat(totalOutput.getPromptFilterResults()).isNotNull(); - assertThat(totalOutput.getChoices().get(0).getContentFilterResults()).isNotNull(); + assertThat(usageRef.get().getTotalTokens()).isGreaterThan(0); + assertThat(usageRef.get().getPromptTokens()).isGreaterThan(0); + assertThat(usageRef.get().getCompletionTokens()).isGreaterThan(0); } @Test void embedding() { final var embedding = service.embedding("Hello world"); - assertThat(embedding.getData().get(0).getEmbedding()).hasSizeGreaterThan(1); - assertThat(embedding.getModel()).isEqualTo("text-embedding-3-small"); - assertThat(embedding.getObject()).isEqualTo("list"); + assertThat(embedding.getOriginalResponse().getData().get(0).getEmbedding()) + .hasSizeGreaterThan(1); + assertThat(embedding.getEmbeddingVectors()).isInstanceOf(ArrayList.class); + assertThat(embedding.getEmbeddingVectors().get(0)).isInstanceOf(float[].class); + + assertThat(embedding.getOriginalResponse().getModel()).isEqualTo("text-embedding-3-small"); + assertThat(embedding.getOriginalResponse().getObject()).isEqualTo("list"); } @Test @@ -87,9 +94,8 @@ void chatCompletionWithResource() { final var completion = service.chatCompletionWithResource("ai-sdk-java-e2e", "Where is the nearest coffee shop?"); - final var message = completion.getChoices().get(0).getMessage(); - assertThat(message.getRole()).isEqualTo("assistant"); - assertThat(message.getContent()).isNotEmpty(); + assertThat(completion.getChoice().getMessage().getRole()).isEqualTo(ASSISTANT); + assertThat(completion.getContent()).isNotEmpty(); } @Test diff --git a/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/OpenAiV2Test.java b/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/OpenAiV2Test.java deleted file mode 100644 index 9f89f1da5..000000000 --- a/sample-code/spring-app/src/test/java/com/sap/ai/sdk/app/controllers/OpenAiV2Test.java +++ /dev/null @@ -1,109 +0,0 @@ -package com.sap.ai.sdk.app.controllers; - -import static com.sap.ai.sdk.foundationmodels.openai.OpenAiModel.GPT_4O_MINI; -import static com.sap.ai.sdk.foundationmodels.openai.generated.model.ChatCompletionResponseMessageRole.ASSISTANT; -import static org.assertj.core.api.Assertions.assertThat; - -import com.sap.ai.sdk.app.services.OpenAiServiceV2; -import com.sap.ai.sdk.foundationmodels.openai.OpenAiChatCompletionRequest; -import com.sap.ai.sdk.foundationmodels.openai.OpenAiClient; -import com.sap.ai.sdk.foundationmodels.openai.OpenAiMessage; -import com.sap.ai.sdk.foundationmodels.openai.generated.model.CompletionUsage; -import java.util.ArrayList; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; -import lombok.extern.slf4j.Slf4j; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -@Slf4j -class OpenAiV2Test { - OpenAiServiceV2 service; - - @BeforeEach - void setUp() { - service = new OpenAiServiceV2(); - } - - @Test - void chatCompletion() { - final var completion = service.chatCompletion("Who is the prettiest"); - - assertThat(completion.getChoice().getMessage().getRole()).isEqualTo(ASSISTANT); - assertThat(completion.getContent()).isNotEmpty(); - } - - @Test - void testMessagesHistory() { - assertThat(service.messagesHistory("What is the capital of France?").getContent()).isNotEmpty(); - } - - @Test - void chatCompletionImage() { - final var completion = - service.chatCompletionImage( - "https://upload.wikimedia.org/wikipedia/commons/thumb/5/59/SAP_2011_logo.svg/440px-SAP_2011_logo.svg.png"); - - assertThat(completion.getContent()).isNotEmpty(); - assertThat(completion.getChoice().getMessage().getRole()).isEqualTo(ASSISTANT); - } - - @Test - void streamChatCompletion() { - final var userMessage = OpenAiMessage.user("Who is the prettiest?"); - final var prompt = new OpenAiChatCompletionRequest(userMessage); - - final var usageRef = new AtomicReference(); - final var filledDeltaCount = new AtomicInteger(0); - - OpenAiClient.forModel(GPT_4O_MINI) - .streamChatCompletionDeltas(prompt) - // foreach consumes all elements, closing the stream at the end - .forEach( - delta -> { - usageRef.compareAndExchange(null, delta.getCompletionUsage()); - final String deltaContent = delta.getDeltaContent(); - log.info("delta: {}", delta); - if (!deltaContent.isEmpty()) { - filledDeltaCount.incrementAndGet(); - } - }); - - assertThat(filledDeltaCount.get()).isGreaterThan(0); - - assertThat(usageRef.get().getTotalTokens()).isGreaterThan(0); - assertThat(usageRef.get().getPromptTokens()).isGreaterThan(0); - assertThat(usageRef.get().getCompletionTokens()).isGreaterThan(0); - } - - @Test - void embedding() { - final var embedding = service.embedding("Hello world"); - - assertThat(embedding.getOriginalResponse().getData().get(0).getEmbedding()) - .hasSizeGreaterThan(1); - assertThat(embedding.getEmbeddingVectors()).isInstanceOf(ArrayList.class); - assertThat(embedding.getEmbeddingVectors().get(0)).isInstanceOf(float[].class); - - assertThat(embedding.getOriginalResponse().getModel()).isEqualTo("text-embedding-3-small"); - assertThat(embedding.getOriginalResponse().getObject()).isEqualTo("list"); - } - - @Test - void chatCompletionWithResource() { - final var completion = - service.chatCompletionWithResource("ai-sdk-java-e2e", "Where is the nearest coffee shop?"); - - assertThat(completion.getChoice().getMessage().getRole()).isEqualTo(ASSISTANT); - assertThat(completion.getContent()).isNotEmpty(); - } - - @Test - void chatCompletionToolExecution() { - final var completion = service.chatCompletionToolExecution("Dubai", "°C"); - - String content = completion.getContent(); - - assertThat(content).contains("°C"); - } -}