diff --git a/.gitignore b/.gitignore index 6c92fd2..dc0edaa 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ pom.xml.versionsBackup BaseTest.java site/ + +application.properties diff --git a/docs/docs/reference/google/gemini/chat/home.md b/docs/docs/reference/google/gemini/chat/home.md new file mode 100644 index 0000000..c83506b --- /dev/null +++ b/docs/docs/reference/google/gemini/chat/home.md @@ -0,0 +1,121 @@ +--- +title: Chat +--- + +> 支持 Google Gemini,产品地址: https://ai.google.dev/gemini-api + +### 简单对话 + +--- + +处理纯文字输入。借助此功能,您可以执行自然语言处理 (NLP) 任务,例如文本补全和摘要。 + +使用示例 + +```java +try (GoogleClient client = GoogleClient.builder() + .apiKey(token) + .build()) { + PartEntity part = PartEntity.builder() + .text("Hello, Open AI Java SDK!") + .build(); + ObjectEntity object = ObjectEntity.builder() + .parts(Lists.newArrayList(part)) + .build(); + ChatEntity chat = ChatEntity.builder() + .contents(Lists.newArrayList(object)) + .build(); + + ChatResponse response = client.createChatCompletions(chat); + response.getCandidates() + .forEach(item -> item.getContent() + .getParts() + .forEach(value -> log.info(value.getText()))); +} +``` + +### 多轮对话(聊天) + +--- + +打造互动式聊天体验。 借助此 API 的聊天功能,您可以收集多轮问题和回复,让用户能够逐步找到答案或获得有关多部分问题的帮助。 + +使用示例 + +```java +List contents = Lists.newArrayList(); +PartEntity part = PartEntity.builder() + .text("你好,我叫小明") + .build(); +ObjectEntity object = ObjectEntity.builder() + .parts(Lists.newArrayList(part)) + .build(); +contents.add(object); +ChatEntity chat = ChatEntity.builder() + .contents(contents) + .build(); +ChatResponse response = client.createChatCompletions(chat); +response.getCandidates() + .forEach(item -> item.getContent() + .getParts() + .forEach(value -> { + log.info(value.getText()); + + contents.add(ObjectEntity.builder() + .role(RoleModel.MODEL) + .parts(Lists.newArrayList(PartEntity.builder() + .text(value.getText()) + .build())) + .build()); + })); + +ObjectEntity newObject = ObjectEntity.builder() + .parts(Lists.newArrayList(PartEntity.builder() + .text("我刚刚说了什么") + .build())) + .build(); +contents.add(newObject); +ChatEntity newChat = ChatEntity.builder() + .contents(contents) + .build(); +client.createChatCompletions(newChat); +``` + +### 流式响应 + +--- + +以数据流的形式接收。流式响应会在模型生成增量数据时将这些数据发送回您的应用。 + +使用示例 + +```java +// 构建客户端 +CountDownLatch countDownLatch = new CountDownLatch(1); +ConsoleEventSourceListener listener = ConsoleEventSourceListener.builder() + .countDownLatch(countDownLatch) + .build(); +GoogleClient client = GoogleClient.builder() + .apiKey(ResourceUtils.getValue("google.token")) + .listener(listener) + .build(); + +List contents = Lists.newArrayList(); +PartEntity part = PartEntity.builder() + .text("帮我写一万字的作文") + .build(); +ObjectEntity object = ObjectEntity.builder() + .parts(Lists.newArrayList(part)) + .build(); + contents.add(object); +ChatEntity chat = ChatEntity.builder() + .contents(contents) + .build(); +client.createChatCompletions(chat); +try { + countDownLatch.await(); +} +catch (InterruptedException e) { + log.error("Interrupted while waiting", e); +} +``` diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 35e3506..007f071 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -21,7 +21,6 @@ theme: name: material custom_dir: overrides - language: en features: - content.code.annotate - content.tabs.link @@ -64,7 +63,7 @@ markdown_extensions: plugins: - i18n: reconfigure_material: true - default_language: en + default_language: zh languages: - locale: zh name: Chinese (Simplified) @@ -111,6 +110,9 @@ nav: - Google PaLM: - reference/google_palm/completions.md - reference/google_palm/chat.md + - Google: + - Gemini: + - reference/google/gemini/chat/home.md - NavReleaseNote: - release/latest.md - release/2024.01.1.md diff --git a/src/main/java/org/devlive/sdk/openai/DefaultApi.java b/src/main/java/org/devlive/sdk/common/DefaultApi.java similarity index 99% rename from src/main/java/org/devlive/sdk/openai/DefaultApi.java rename to src/main/java/org/devlive/sdk/common/DefaultApi.java index 0bdbbc4..6a50e30 100644 --- a/src/main/java/org/devlive/sdk/openai/DefaultApi.java +++ b/src/main/java/org/devlive/sdk/common/DefaultApi.java @@ -1,4 +1,4 @@ -package org.devlive.sdk.openai; +package org.devlive.sdk.common; import io.reactivex.Single; import okhttp3.MultipartBody; diff --git a/src/main/java/org/devlive/sdk/openai/DefaultClient.java b/src/main/java/org/devlive/sdk/common/DefaultClient.java similarity index 99% rename from src/main/java/org/devlive/sdk/openai/DefaultClient.java rename to src/main/java/org/devlive/sdk/common/DefaultClient.java index 055f814..0e01c2f 100644 --- a/src/main/java/org/devlive/sdk/openai/DefaultClient.java +++ b/src/main/java/org/devlive/sdk/common/DefaultClient.java @@ -1,4 +1,4 @@ -package org.devlive.sdk.openai; +package org.devlive.sdk.common; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.Lists; @@ -12,6 +12,7 @@ import okhttp3.sse.EventSourceListener; import okhttp3.sse.EventSources; import org.apache.commons.lang3.ObjectUtils; +import org.devlive.sdk.common.exception.RequestException; import org.devlive.sdk.openai.entity.AudioEntity; import org.devlive.sdk.openai.entity.ChatEntity; import org.devlive.sdk.openai.entity.CompletionEntity; @@ -28,7 +29,6 @@ import org.devlive.sdk.openai.entity.beta.QueryEntity; import org.devlive.sdk.openai.entity.beta.ThreadEntity; import org.devlive.sdk.openai.entity.google.MessageEntity; -import org.devlive.sdk.openai.exception.RequestException; import org.devlive.sdk.openai.mixin.IgnoreUnknownMixin; import org.devlive.sdk.openai.model.ProviderModel; import org.devlive.sdk.openai.model.UrlModel; diff --git a/src/main/java/org/devlive/sdk/openai/exception/AuthorizedException.java b/src/main/java/org/devlive/sdk/common/exception/AuthorizedException.java similarity index 78% rename from src/main/java/org/devlive/sdk/openai/exception/AuthorizedException.java rename to src/main/java/org/devlive/sdk/common/exception/AuthorizedException.java index c5794ac..f9b1dd0 100644 --- a/src/main/java/org/devlive/sdk/openai/exception/AuthorizedException.java +++ b/src/main/java/org/devlive/sdk/common/exception/AuthorizedException.java @@ -1,4 +1,4 @@ -package org.devlive.sdk.openai.exception; +package org.devlive.sdk.common.exception; public class AuthorizedException extends DefaultException diff --git a/src/main/java/org/devlive/sdk/openai/exception/DefaultException.java b/src/main/java/org/devlive/sdk/common/exception/DefaultException.java similarity index 84% rename from src/main/java/org/devlive/sdk/openai/exception/DefaultException.java rename to src/main/java/org/devlive/sdk/common/exception/DefaultException.java index c2f64c5..524688d 100644 --- a/src/main/java/org/devlive/sdk/openai/exception/DefaultException.java +++ b/src/main/java/org/devlive/sdk/common/exception/DefaultException.java @@ -1,4 +1,4 @@ -package org.devlive.sdk.openai.exception; +package org.devlive.sdk.common.exception; import lombok.Getter; diff --git a/src/main/java/org/devlive/sdk/openai/exception/ParamException.java b/src/main/java/org/devlive/sdk/common/exception/ParamException.java similarity index 77% rename from src/main/java/org/devlive/sdk/openai/exception/ParamException.java rename to src/main/java/org/devlive/sdk/common/exception/ParamException.java index e0c5376..c3a8d3e 100644 --- a/src/main/java/org/devlive/sdk/openai/exception/ParamException.java +++ b/src/main/java/org/devlive/sdk/common/exception/ParamException.java @@ -1,4 +1,4 @@ -package org.devlive.sdk.openai.exception; +package org.devlive.sdk.common.exception; public class ParamException extends DefaultException diff --git a/src/main/java/org/devlive/sdk/openai/exception/RequestException.java b/src/main/java/org/devlive/sdk/common/exception/RequestException.java similarity index 77% rename from src/main/java/org/devlive/sdk/openai/exception/RequestException.java rename to src/main/java/org/devlive/sdk/common/exception/RequestException.java index 8da6020..a74dd8e 100644 --- a/src/main/java/org/devlive/sdk/openai/exception/RequestException.java +++ b/src/main/java/org/devlive/sdk/common/exception/RequestException.java @@ -1,4 +1,4 @@ -package org.devlive.sdk.openai.exception; +package org.devlive.sdk.common.exception; public class RequestException extends DefaultException diff --git a/src/main/java/org/devlive/sdk/openai/interceptor/DefaultInterceptor.java b/src/main/java/org/devlive/sdk/common/interceptor/DefaultInterceptor.java similarity index 92% rename from src/main/java/org/devlive/sdk/openai/interceptor/DefaultInterceptor.java rename to src/main/java/org/devlive/sdk/common/interceptor/DefaultInterceptor.java index 43a04e4..394f506 100644 --- a/src/main/java/org/devlive/sdk/openai/interceptor/DefaultInterceptor.java +++ b/src/main/java/org/devlive/sdk/common/interceptor/DefaultInterceptor.java @@ -1,4 +1,4 @@ -package org.devlive.sdk.openai.interceptor; +package org.devlive.sdk.common.interceptor; import com.google.common.base.Preconditions; import lombok.Getter; @@ -11,8 +11,8 @@ import okhttp3.ResponseBody; import okio.Buffer; import org.apache.commons.lang3.ObjectUtils; -import org.devlive.sdk.openai.exception.AuthorizedException; -import org.devlive.sdk.openai.exception.RequestException; +import org.devlive.sdk.common.exception.AuthorizedException; +import org.devlive.sdk.common.exception.RequestException; import org.devlive.sdk.openai.response.DefaultResponse; import org.devlive.sdk.openai.utils.JsonUtils; @@ -44,7 +44,7 @@ public Response intercept(Chain chain) if (ObjectUtils.isNotEmpty(requestBody)) { Buffer buffer = new Buffer(); requestBody.writeTo(buffer); - log.debug("Request body {}", buffer.readUtf8()); + log.info("Request body {}", buffer.readUtf8()); } Response response = chain.proceed(request); diff --git a/src/main/java/org/devlive/sdk/openai/listener/ConsoleEventSourceListener.java b/src/main/java/org/devlive/sdk/common/listener/ConsoleEventSourceListener.java similarity index 98% rename from src/main/java/org/devlive/sdk/openai/listener/ConsoleEventSourceListener.java rename to src/main/java/org/devlive/sdk/common/listener/ConsoleEventSourceListener.java index 36af54c..cc6b917 100644 --- a/src/main/java/org/devlive/sdk/openai/listener/ConsoleEventSourceListener.java +++ b/src/main/java/org/devlive/sdk/common/listener/ConsoleEventSourceListener.java @@ -1,4 +1,4 @@ -package org.devlive.sdk.openai.listener; +package org.devlive.sdk.common.listener; import lombok.Builder; import lombok.extern.slf4j.Slf4j; diff --git a/src/main/java/org/devlive/sdk/openai/listener/HttpServletEventSourceListener.java b/src/main/java/org/devlive/sdk/common/listener/HttpServletEventSourceListener.java similarity index 97% rename from src/main/java/org/devlive/sdk/openai/listener/HttpServletEventSourceListener.java rename to src/main/java/org/devlive/sdk/common/listener/HttpServletEventSourceListener.java index b08984c..bdaf617 100644 --- a/src/main/java/org/devlive/sdk/openai/listener/HttpServletEventSourceListener.java +++ b/src/main/java/org/devlive/sdk/common/listener/HttpServletEventSourceListener.java @@ -1,4 +1,4 @@ -package org.devlive.sdk.openai.listener; +package org.devlive.sdk.common.listener; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @@ -9,7 +9,7 @@ import okhttp3.sse.EventSource; import okhttp3.sse.EventSourceListener; import org.apache.commons.lang3.ObjectUtils; -import org.devlive.sdk.openai.exception.ParamException; +import org.devlive.sdk.common.exception.ParamException; import org.devlive.sdk.openai.response.CompleteResponse; import org.devlive.sdk.openai.utils.JsonUtils; diff --git a/src/main/java/org/devlive/sdk/openai/utils/HttpUrlUtils.java b/src/main/java/org/devlive/sdk/common/utils/HttpUrlUtils.java similarity index 94% rename from src/main/java/org/devlive/sdk/openai/utils/HttpUrlUtils.java rename to src/main/java/org/devlive/sdk/common/utils/HttpUrlUtils.java index 025eb8a..0a09eb9 100644 --- a/src/main/java/org/devlive/sdk/openai/utils/HttpUrlUtils.java +++ b/src/main/java/org/devlive/sdk/common/utils/HttpUrlUtils.java @@ -1,4 +1,4 @@ -package org.devlive.sdk.openai.utils; +package org.devlive.sdk.common.utils; import okhttp3.HttpUrl; diff --git a/src/main/java/org/devlive/sdk/common/utils/ValidateUtils.java b/src/main/java/org/devlive/sdk/common/utils/ValidateUtils.java new file mode 100644 index 0000000..c874af1 --- /dev/null +++ b/src/main/java/org/devlive/sdk/common/utils/ValidateUtils.java @@ -0,0 +1,32 @@ +package org.devlive.sdk.common.utils; + +import org.apache.commons.lang3.StringUtils; +import org.devlive.sdk.common.exception.ParamException; + +public class ValidateUtils +{ + private ValidateUtils() + { + } + + /** + * Validate host + * + * @param host Original host + * @param defaultHost Default host + * @return host + */ + public static String validateHost(String host, String defaultHost) + { + if (StringUtils.isEmpty(host)) { + return defaultHost; + } + else { + boolean flag = host.startsWith("http") || host.startsWith("https"); + if (!flag) { + throw new ParamException(String.format("Invalid apiHost <%s> must start with http or https", host)); + } + } + return host; + } +} diff --git a/src/main/java/org/devlive/sdk/openai/OpenAiClient.java b/src/main/java/org/devlive/sdk/openai/OpenAiClient.java index 4ae0c2d..ccb508b 100644 --- a/src/main/java/org/devlive/sdk/openai/OpenAiClient.java +++ b/src/main/java/org/devlive/sdk/openai/OpenAiClient.java @@ -8,10 +8,12 @@ import okhttp3.sse.EventSourceListener; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; -import org.devlive.sdk.openai.exception.ParamException; +import org.devlive.sdk.common.DefaultApi; +import org.devlive.sdk.common.DefaultClient; +import org.devlive.sdk.common.exception.ParamException; +import org.devlive.sdk.common.interceptor.DefaultInterceptor; import org.devlive.sdk.openai.interceptor.AzureInterceptor; import org.devlive.sdk.openai.interceptor.ClaudeInterceptor; -import org.devlive.sdk.openai.interceptor.DefaultInterceptor; import org.devlive.sdk.openai.interceptor.GooglePaLMInterceptor; import org.devlive.sdk.openai.interceptor.OpenAiInterceptor; import org.devlive.sdk.openai.model.CompletionModel; diff --git a/src/main/java/org/devlive/sdk/openai/entity/AudioEntity.java b/src/main/java/org/devlive/sdk/openai/entity/AudioEntity.java index 6f5a04a..9d1cec5 100644 --- a/src/main/java/org/devlive/sdk/openai/entity/AudioEntity.java +++ b/src/main/java/org/devlive/sdk/openai/entity/AudioEntity.java @@ -12,7 +12,7 @@ import org.apache.commons.lang3.EnumUtils; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; -import org.devlive.sdk.openai.exception.ParamException; +import org.devlive.sdk.common.exception.ParamException; import org.devlive.sdk.openai.model.AudioFormatModel; import org.devlive.sdk.openai.model.AudioModel; import org.devlive.sdk.openai.utils.FileUtils; diff --git a/src/main/java/org/devlive/sdk/openai/entity/ChatEntity.java b/src/main/java/org/devlive/sdk/openai/entity/ChatEntity.java index 02919fd..4bfd366 100644 --- a/src/main/java/org/devlive/sdk/openai/entity/ChatEntity.java +++ b/src/main/java/org/devlive/sdk/openai/entity/ChatEntity.java @@ -8,7 +8,7 @@ import lombok.NoArgsConstructor; import lombok.ToString; import org.apache.commons.lang3.ObjectUtils; -import org.devlive.sdk.openai.exception.ParamException; +import org.devlive.sdk.common.exception.ParamException; import org.devlive.sdk.openai.model.CompletionModel; import org.devlive.sdk.openai.utils.EnumsUtils; diff --git a/src/main/java/org/devlive/sdk/openai/entity/CompletionEntity.java b/src/main/java/org/devlive/sdk/openai/entity/CompletionEntity.java index 267644b..ab944f8 100644 --- a/src/main/java/org/devlive/sdk/openai/entity/CompletionEntity.java +++ b/src/main/java/org/devlive/sdk/openai/entity/CompletionEntity.java @@ -9,7 +9,7 @@ import lombok.ToString; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; -import org.devlive.sdk.openai.exception.ParamException; +import org.devlive.sdk.common.exception.ParamException; import org.devlive.sdk.openai.model.CompletionModel; import org.devlive.sdk.openai.utils.EnumsUtils; diff --git a/src/main/java/org/devlive/sdk/openai/entity/EditEntity.java b/src/main/java/org/devlive/sdk/openai/entity/EditEntity.java index 1b13b63..2a15736 100644 --- a/src/main/java/org/devlive/sdk/openai/entity/EditEntity.java +++ b/src/main/java/org/devlive/sdk/openai/entity/EditEntity.java @@ -9,7 +9,7 @@ import lombok.ToString; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; -import org.devlive.sdk.openai.exception.ParamException; +import org.devlive.sdk.common.exception.ParamException; import org.devlive.sdk.openai.model.EditModel; import java.util.Arrays; diff --git a/src/main/java/org/devlive/sdk/openai/entity/EmbeddingEntity.java b/src/main/java/org/devlive/sdk/openai/entity/EmbeddingEntity.java index 1281624..c4b8b37 100644 --- a/src/main/java/org/devlive/sdk/openai/entity/EmbeddingEntity.java +++ b/src/main/java/org/devlive/sdk/openai/entity/EmbeddingEntity.java @@ -8,7 +8,7 @@ import lombok.NoArgsConstructor; import lombok.ToString; import org.apache.commons.lang3.StringUtils; -import org.devlive.sdk.openai.exception.ParamException; +import org.devlive.sdk.common.exception.ParamException; import java.util.List; diff --git a/src/main/java/org/devlive/sdk/openai/entity/FileEntity.java b/src/main/java/org/devlive/sdk/openai/entity/FileEntity.java index 5519784..f2d18e3 100644 --- a/src/main/java/org/devlive/sdk/openai/entity/FileEntity.java +++ b/src/main/java/org/devlive/sdk/openai/entity/FileEntity.java @@ -11,7 +11,7 @@ import lombok.ToString; import okhttp3.RequestBody; import org.apache.commons.lang3.ObjectUtils; -import org.devlive.sdk.openai.exception.ParamException; +import org.devlive.sdk.common.exception.ParamException; import org.devlive.sdk.openai.model.PurposeModel; import org.devlive.sdk.openai.utils.MultipartBodyUtils; diff --git a/src/main/java/org/devlive/sdk/openai/entity/FineTuningEntity.java b/src/main/java/org/devlive/sdk/openai/entity/FineTuningEntity.java index b5a4297..37ea7f2 100644 --- a/src/main/java/org/devlive/sdk/openai/entity/FineTuningEntity.java +++ b/src/main/java/org/devlive/sdk/openai/entity/FineTuningEntity.java @@ -8,7 +8,7 @@ import lombok.Data; import lombok.NoArgsConstructor; import lombok.ToString; -import org.devlive.sdk.openai.exception.ParamException; +import org.devlive.sdk.common.exception.ParamException; import org.devlive.sdk.openai.model.CompletionModel; @Data diff --git a/src/main/java/org/devlive/sdk/openai/entity/ImageEntity.java b/src/main/java/org/devlive/sdk/openai/entity/ImageEntity.java index 7f4d282..724a59d 100644 --- a/src/main/java/org/devlive/sdk/openai/entity/ImageEntity.java +++ b/src/main/java/org/devlive/sdk/openai/entity/ImageEntity.java @@ -12,7 +12,7 @@ import org.apache.commons.lang3.EnumUtils; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; -import org.devlive.sdk.openai.exception.ParamException; +import org.devlive.sdk.common.exception.ParamException; import org.devlive.sdk.openai.model.ImageFormatModel; import org.devlive.sdk.openai.model.ImageSizeModel; import org.devlive.sdk.openai.utils.MultipartBodyUtils; diff --git a/src/main/java/org/devlive/sdk/openai/entity/MessageEntity.java b/src/main/java/org/devlive/sdk/openai/entity/MessageEntity.java index 6d72361..1a249a2 100644 --- a/src/main/java/org/devlive/sdk/openai/entity/MessageEntity.java +++ b/src/main/java/org/devlive/sdk/openai/entity/MessageEntity.java @@ -9,7 +9,7 @@ import lombok.ToString; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; -import org.devlive.sdk.openai.exception.ParamException; +import org.devlive.sdk.common.exception.ParamException; import org.devlive.sdk.openai.model.MessageModel; import org.devlive.sdk.openai.utils.EnumsUtils; diff --git a/src/main/java/org/devlive/sdk/openai/entity/ModerationEntity.java b/src/main/java/org/devlive/sdk/openai/entity/ModerationEntity.java index ba5ee89..0933afa 100644 --- a/src/main/java/org/devlive/sdk/openai/entity/ModerationEntity.java +++ b/src/main/java/org/devlive/sdk/openai/entity/ModerationEntity.java @@ -9,7 +9,7 @@ import lombok.ToString; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; -import org.devlive.sdk.openai.exception.ParamException; +import org.devlive.sdk.common.exception.ParamException; import org.devlive.sdk.openai.model.ModerationModel; import java.util.List; diff --git a/src/main/java/org/devlive/sdk/openai/entity/beta/AssistantsEntity.java b/src/main/java/org/devlive/sdk/openai/entity/beta/AssistantsEntity.java index 5dac22e..077391e 100644 --- a/src/main/java/org/devlive/sdk/openai/entity/beta/AssistantsEntity.java +++ b/src/main/java/org/devlive/sdk/openai/entity/beta/AssistantsEntity.java @@ -8,7 +8,7 @@ import lombok.Data; import lombok.NoArgsConstructor; import lombok.ToString; -import org.devlive.sdk.openai.exception.ParamException; +import org.devlive.sdk.common.exception.ParamException; import org.devlive.sdk.openai.model.CompletionModel; import java.util.Map; diff --git a/src/main/java/org/devlive/sdk/openai/interceptor/AzureInterceptor.java b/src/main/java/org/devlive/sdk/openai/interceptor/AzureInterceptor.java index b05e8e9..6736b2b 100644 --- a/src/main/java/org/devlive/sdk/openai/interceptor/AzureInterceptor.java +++ b/src/main/java/org/devlive/sdk/openai/interceptor/AzureInterceptor.java @@ -4,7 +4,8 @@ import okhttp3.HttpUrl; import okhttp3.Request; import org.apache.commons.lang3.StringUtils; -import org.devlive.sdk.openai.exception.ParamException; +import org.devlive.sdk.common.exception.ParamException; +import org.devlive.sdk.common.interceptor.DefaultInterceptor; import java.util.List; diff --git a/src/main/java/org/devlive/sdk/openai/interceptor/ClaudeInterceptor.java b/src/main/java/org/devlive/sdk/openai/interceptor/ClaudeInterceptor.java index 06bc13b..46b5be4 100644 --- a/src/main/java/org/devlive/sdk/openai/interceptor/ClaudeInterceptor.java +++ b/src/main/java/org/devlive/sdk/openai/interceptor/ClaudeInterceptor.java @@ -3,7 +3,8 @@ import lombok.extern.slf4j.Slf4j; import okhttp3.Request; import org.apache.commons.lang3.StringUtils; -import org.devlive.sdk.openai.exception.ParamException; +import org.devlive.sdk.common.exception.ParamException; +import org.devlive.sdk.common.interceptor.DefaultInterceptor; @Slf4j public class ClaudeInterceptor diff --git a/src/main/java/org/devlive/sdk/openai/interceptor/GooglePaLMInterceptor.java b/src/main/java/org/devlive/sdk/openai/interceptor/GooglePaLMInterceptor.java index b6c3d7d..8cd8778 100644 --- a/src/main/java/org/devlive/sdk/openai/interceptor/GooglePaLMInterceptor.java +++ b/src/main/java/org/devlive/sdk/openai/interceptor/GooglePaLMInterceptor.java @@ -5,13 +5,18 @@ import okhttp3.HttpUrl; import okhttp3.Request; import org.apache.commons.lang3.StringUtils; -import org.devlive.sdk.openai.exception.ParamException; +import org.devlive.sdk.common.exception.ParamException; +import org.devlive.sdk.common.interceptor.DefaultInterceptor; +import org.devlive.sdk.common.utils.HttpUrlUtils; import org.devlive.sdk.openai.model.CompletionModel; import org.devlive.sdk.openai.utils.EnumsUtils; -import org.devlive.sdk.openai.utils.HttpUrlUtils; import java.util.List; +/** + * Officially obsolete and not recommended for use + */ +@Deprecated @Slf4j public class GooglePaLMInterceptor extends DefaultInterceptor diff --git a/src/main/java/org/devlive/sdk/openai/interceptor/OpenAiInterceptor.java b/src/main/java/org/devlive/sdk/openai/interceptor/OpenAiInterceptor.java index 5cb4804..76893f5 100644 --- a/src/main/java/org/devlive/sdk/openai/interceptor/OpenAiInterceptor.java +++ b/src/main/java/org/devlive/sdk/openai/interceptor/OpenAiInterceptor.java @@ -3,7 +3,8 @@ import lombok.extern.slf4j.Slf4j; import okhttp3.Request; import org.apache.commons.lang3.StringUtils; -import org.devlive.sdk.openai.exception.ParamException; +import org.devlive.sdk.common.exception.ParamException; +import org.devlive.sdk.common.interceptor.DefaultInterceptor; @Slf4j public class OpenAiInterceptor diff --git a/src/main/java/org/devlive/sdk/openai/model/CompletionModel.java b/src/main/java/org/devlive/sdk/openai/model/CompletionModel.java index ae77b7d..11e25d5 100644 --- a/src/main/java/org/devlive/sdk/openai/model/CompletionModel.java +++ b/src/main/java/org/devlive/sdk/openai/model/CompletionModel.java @@ -64,6 +64,7 @@ public enum CompletionModel "Most capable model in the GPT-3 series. Can perform any task the other GPT-3 models can, often with higher quality, longer output and better instruction-following. It can process up to 4,000 tokens per request.", "Complex intent, cause and effect, creative generation, search, summarization for audience", 4097), + @Deprecated TEXT_DAVINCI_003("text-davinci-003", "Most capable model in the GPT-3 series. Can perform any task the other GPT-3 models can, often with higher quality, longer output and better instruction-following. It can process up to 4,000 tokens per request.", "Complex intent, cause and effect, creative generation, search, summarization for audience", @@ -134,7 +135,7 @@ public enum CompletionModel null, 32768), - GPT_4O("gpt-4o", + GPT_4O("gpt-4o", "most advanced, multimodal flagship model that’s cheaper and faster than GPT-4 Turbo. Currently points to gpt-4o-2024-05-13", null, 128000), diff --git a/src/main/java/org/devlive/sdk/openai/model/ProviderModel.java b/src/main/java/org/devlive/sdk/openai/model/ProviderModel.java index 96dada5..862eef6 100644 --- a/src/main/java/org/devlive/sdk/openai/model/ProviderModel.java +++ b/src/main/java/org/devlive/sdk/openai/model/ProviderModel.java @@ -21,5 +21,7 @@ public enum ProviderModel * Google PaLM * https://makersuite.google.com */ - GOOGLE_PALM + @Deprecated + GOOGLE_PALM, + GOOGLE_GEMINI } diff --git a/src/main/java/org/devlive/sdk/openai/utils/ProviderUtils.java b/src/main/java/org/devlive/sdk/openai/utils/ProviderUtils.java index dc0aefe..1f8bc5b 100644 --- a/src/main/java/org/devlive/sdk/openai/utils/ProviderUtils.java +++ b/src/main/java/org/devlive/sdk/openai/utils/ProviderUtils.java @@ -1,6 +1,6 @@ package org.devlive.sdk.openai.utils; -import org.devlive.sdk.openai.exception.RequestException; +import org.devlive.sdk.common.exception.RequestException; import org.devlive.sdk.openai.model.ProviderModel; import org.devlive.sdk.openai.model.UrlModel; diff --git a/src/main/java/org/devlive/sdk/platform/google/GoogleApi.java b/src/main/java/org/devlive/sdk/platform/google/GoogleApi.java new file mode 100644 index 0000000..cdb290b --- /dev/null +++ b/src/main/java/org/devlive/sdk/platform/google/GoogleApi.java @@ -0,0 +1,17 @@ +package org.devlive.sdk.platform.google; + +import io.reactivex.Single; +import org.devlive.sdk.common.DefaultApi; +import org.devlive.sdk.platform.google.entity.ChatEntity; +import org.devlive.sdk.platform.google.response.ChatResponse; +import retrofit2.http.Body; +import retrofit2.http.POST; +import retrofit2.http.Url; + +public interface GoogleApi + extends DefaultApi +{ + @POST + Single fetchChatCompletions(@Url String url, + @Body ChatEntity configure); +} diff --git a/src/main/java/org/devlive/sdk/platform/google/GoogleClient.java b/src/main/java/org/devlive/sdk/platform/google/GoogleClient.java new file mode 100644 index 0000000..9b336ed --- /dev/null +++ b/src/main/java/org/devlive/sdk/platform/google/GoogleClient.java @@ -0,0 +1,209 @@ +package org.devlive.sdk.platform.google; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.Builder; +import lombok.extern.slf4j.Slf4j; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.sse.EventSource; +import okhttp3.sse.EventSourceListener; +import okhttp3.sse.EventSources; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +import org.devlive.sdk.common.DefaultClient; +import org.devlive.sdk.common.exception.ParamException; +import org.devlive.sdk.common.exception.RequestException; +import org.devlive.sdk.common.utils.ValidateUtils; +import org.devlive.sdk.openai.mixin.IgnoreUnknownMixin; +import org.devlive.sdk.openai.model.ProviderModel; +import org.devlive.sdk.openai.model.UrlModel; +import org.devlive.sdk.openai.utils.MultipartBodyUtils; +import org.devlive.sdk.openai.utils.ProviderUtils; +import org.devlive.sdk.platform.google.entity.ChatEntity; +import org.devlive.sdk.platform.google.interceptor.GoogleInterceptor; +import org.devlive.sdk.platform.google.model.GenerativeModel; +import org.devlive.sdk.platform.google.model.VersionModel; +import org.devlive.sdk.platform.google.response.ChatResponse; +import retrofit2.Retrofit; +import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; +import retrofit2.converter.jackson.JacksonConverterFactory; + +import java.util.concurrent.TimeUnit; + +@Slf4j +@Builder +public class GoogleClient + extends DefaultClient +{ + private final ObjectMapper objectMapper = new ObjectMapper(); + + private String apiKey; + private String apiHost; + private Integer timeout; + private TimeUnit unit; + private OkHttpClient client; + private String model; + private VersionModel version; + private GoogleApi api; + private EventSourceListener listener; + + private GoogleClient(GoogleClientBuilder builder) + { + boolean hasApiKey = StringUtils.isNotEmpty(builder.apiKey); + if (!hasApiKey) { + log.error("Invalid Google token"); + throw new ParamException("Invalid Google token"); + } + + if (ObjectUtils.isEmpty(builder.apiHost)) { + builder.apiHost(null); + } + if (ObjectUtils.isEmpty(builder.timeout)) { + builder.timeout(null); + } + if (ObjectUtils.isEmpty(builder.unit)) { + builder.unit(null); + } + + if (ObjectUtils.isEmpty(builder.version)) { + builder.version(VersionModel.V1BETA); + } + this.version = builder.version; + + if (ObjectUtils.isEmpty(builder.model)) { + builder.model(GenerativeModel.GEMINI_PRO); + } + this.model = builder.model; + + if (ObjectUtils.isEmpty(builder.listener)) { + builder.listener(null); + } + super.listener = builder.listener; + this.listener = builder.listener; + + if (ObjectUtils.isEmpty(builder.client)) { + builder.client(null); + } + + super.client = builder.client; + this.client = builder.client; + super.apiHost = builder.apiHost; + this.apiHost = builder.apiHost; + super.provider = ProviderModel.GOOGLE_GEMINI; + + objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + this.api = new Retrofit.Builder() + .baseUrl(builder.apiHost) + .client(builder.client) + .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) + .addConverterFactory(JacksonConverterFactory.create(objectMapper)) + .build() + .create(GoogleApi.class); + } + + public ChatResponse createChatCompletions(ChatEntity configure) + { + String url = ProviderUtils.getUrl(provider, UrlModel.FETCH_CHAT_COMPLETIONS); + if (ObjectUtils.isNotEmpty(this.listener)) { + this.createEventSource(url, configure); + return null; + } + + return this.api.fetchChatCompletions(url, configure) + .blockingGet(); + } + + private ObjectMapper createObjectMapper() + { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.addMixIn(Object.class, IgnoreUnknownMixin.class); + return objectMapper; + } + + private void createEventSource(String url, Object configure) + { + try { + EventSource.Factory factory = EventSources.createFactory(this.client); + ObjectMapper mapper = this.createObjectMapper(); + Request request = new Request.Builder() + .url(String.join("/", this.apiHost, url)) + .post(RequestBody.create(MultipartBodyUtils.JSON, mapper.writeValueAsString(configure))) + .build(); + factory.newEventSource(request, this.listener); + } + catch (Exception e) { + throw new RequestException(String.format("Failed to create event source: %s", e.getMessage())); + } + } + + public static class GoogleClientBuilder + { + public GoogleClientBuilder apiKey(String apiKey) + { + this.apiKey = apiKey; + return this; + } + + public GoogleClientBuilder apiHost(String apiHost) + { + this.apiHost = ValidateUtils.validateHost(apiHost, "https://generativelanguage.googleapis.com"); + return this; + } + + public GoogleClientBuilder timeout(Integer timeout) + { + if (ObjectUtils.isEmpty(timeout)) { + timeout = 30; + } + this.timeout = timeout; + return this; + } + + public GoogleClientBuilder unit(TimeUnit unit) + { + if (ObjectUtils.isEmpty(unit)) { + unit = TimeUnit.SECONDS; + } + this.unit = unit; + return this; + } + + public GoogleClientBuilder client(OkHttpClient client) + { + if (ObjectUtils.isEmpty(client)) { + log.debug("No client specified, creating default client"); + client = new OkHttpClient.Builder() + .connectTimeout(this.timeout, this.unit) + .writeTimeout(this.timeout, this.unit) + .readTimeout(this.timeout, this.unit) + .callTimeout(this.timeout, this.unit) + .build(); + } + GoogleInterceptor interceptor = new GoogleInterceptor(); + interceptor.setApiKey(apiKey); + interceptor.setVersion(version); + interceptor.setModel(model); + if (listener != null) { + interceptor.setStream(true); + } + client = client.newBuilder() + .addInterceptor(interceptor) + .build(); + this.client = client; + return this; + } + + public GoogleClientBuilder model(GenerativeModel model) + { + this.model = model.getName(); + return this; + } + + public GoogleClient build() + { + return new GoogleClient(this); + } + } +} diff --git a/src/main/java/org/devlive/sdk/platform/google/entity/ChatEntity.java b/src/main/java/org/devlive/sdk/platform/google/entity/ChatEntity.java new file mode 100644 index 0000000..963fc11 --- /dev/null +++ b/src/main/java/org/devlive/sdk/platform/google/entity/ChatEntity.java @@ -0,0 +1,47 @@ +package org.devlive.sdk.platform.google.entity; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; +import org.devlive.sdk.common.exception.ParamException; + +import java.util.List; + +@Data +@Builder +@ToString +@NoArgsConstructor +@AllArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +public class ChatEntity +{ + @JsonProperty(value = "contents") + private List contents; + + private ChatEntity(ChatEntityBuilder builder) + { + this.contents = builder.contents; + } + + public static class ChatEntityBuilder + { + public ChatEntityBuilder contents(List contents) + { + if (contents == null || contents.isEmpty()) { + throw new ParamException("Invalid contents: " + contents); + } + + this.contents = contents; + return this; + } + + public ChatEntity build() + { + return new ChatEntity(this); + } + } +} diff --git a/src/main/java/org/devlive/sdk/platform/google/entity/ObjectEntity.java b/src/main/java/org/devlive/sdk/platform/google/entity/ObjectEntity.java new file mode 100644 index 0000000..df7d927 --- /dev/null +++ b/src/main/java/org/devlive/sdk/platform/google/entity/ObjectEntity.java @@ -0,0 +1,63 @@ +package org.devlive.sdk.platform.google.entity; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; +import org.apache.commons.lang3.ObjectUtils; +import org.devlive.sdk.common.exception.ParamException; +import org.devlive.sdk.platform.google.model.RoleModel; + +import java.util.List; + +@Data +@Builder +@ToString +@NoArgsConstructor +@AllArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +public class ObjectEntity +{ + @JsonProperty(value = "role") + private String role; + + @JsonProperty(value = "parts") + private List parts; + + private ObjectEntity(ObjectEntityBuilder builder) + { + if (ObjectUtils.isEmpty(builder.role)) { + builder.role(RoleModel.USER); + } + this.role = builder.role; + + this.parts = builder.parts; + } + + public static class ObjectEntityBuilder + { + public ObjectEntityBuilder role(RoleModel model) + { + this.role = model.getValue(); + return this; + } + + public ObjectEntityBuilder parts(List parts) + { + if (parts == null || parts.isEmpty()) { + throw new ParamException("Invalid parts: " + parts); + } + + this.parts = parts; + return this; + } + + public ObjectEntity build() + { + return new ObjectEntity(this); + } + } +} diff --git a/src/main/java/org/devlive/sdk/platform/google/entity/PartEntity.java b/src/main/java/org/devlive/sdk/platform/google/entity/PartEntity.java new file mode 100644 index 0000000..76ac873 --- /dev/null +++ b/src/main/java/org/devlive/sdk/platform/google/entity/PartEntity.java @@ -0,0 +1,46 @@ +package org.devlive.sdk.platform.google.entity; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; +import org.apache.commons.lang3.StringUtils; +import org.devlive.sdk.common.exception.ParamException; + +@Data +@Builder +@ToString +@NoArgsConstructor +@AllArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +public class PartEntity +{ + @JsonProperty(value = "text") + private String text; + + private PartEntity(PartEntityBuilder builder) + { + this.text = builder.text; + } + + public static class PartEntityBuilder + { + public PartEntityBuilder text(String text) + { + if (StringUtils.isBlank(text)) { + throw new ParamException("Invalid text: " + text); + } + + this.text = text; + return this; + } + + public PartEntity build() + { + return new PartEntity(this); + } + } +} diff --git a/src/main/java/org/devlive/sdk/platform/google/interceptor/GoogleInterceptor.java b/src/main/java/org/devlive/sdk/platform/google/interceptor/GoogleInterceptor.java new file mode 100644 index 0000000..fdcbf27 --- /dev/null +++ b/src/main/java/org/devlive/sdk/platform/google/interceptor/GoogleInterceptor.java @@ -0,0 +1,66 @@ +package org.devlive.sdk.platform.google.interceptor; + +import com.google.common.collect.Lists; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import okhttp3.HttpUrl; +import okhttp3.Request; +import org.apache.commons.lang3.StringUtils; +import org.devlive.sdk.common.exception.ParamException; +import org.devlive.sdk.common.interceptor.DefaultInterceptor; +import org.devlive.sdk.common.utils.HttpUrlUtils; +import org.devlive.sdk.platform.google.model.VersionModel; + +import java.util.List; + +@Slf4j +public class GoogleInterceptor + extends DefaultInterceptor +{ + @Setter + private VersionModel version; + @Setter + private Boolean stream = false; + + public GoogleInterceptor() + { + log.info("Google Interceptor"); + } + + @Override + protected Request prepared(Request original) + { + if (StringUtils.isEmpty(this.getApiKey())) { + log.error("Invalid Google token, must be non-empty"); + throw new ParamException("Invalid Google token, must be non-empty"); + } + + HttpUrl httpUrl = original.url(); + List pathSegments = Lists.newArrayList(); + httpUrl = HttpUrlUtils.removePathSegment(httpUrl); + // https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent?key=YOUR_API_KEY + pathSegments.add(0, String.join(":", getModel(), stream ? "streamGenerateContent" : "generateContent")); + pathSegments.add(0, "models"); + pathSegments.add(0, version.getVersion()); + httpUrl = httpUrl.newBuilder() + .host(httpUrl.host()) + .port(httpUrl.port()) + .addPathSegments(String.join("/", pathSegments)) + .addQueryParameter("key", this.getApiKey()) + .build(); + + if (stream) { + httpUrl = httpUrl.newBuilder() + .addQueryParameter("alt", "sse") + .build(); + } + + log.info("Google interceptor request url {}", httpUrl); + + return original.newBuilder() + .header("Content-Type", "application/json") + .url(httpUrl) + .method(original.method(), original.body()) + .build(); + } +} diff --git a/src/main/java/org/devlive/sdk/platform/google/model/GenerativeModel.java b/src/main/java/org/devlive/sdk/platform/google/model/GenerativeModel.java new file mode 100644 index 0000000..b57dbe9 --- /dev/null +++ b/src/main/java/org/devlive/sdk/platform/google/model/GenerativeModel.java @@ -0,0 +1,19 @@ +package org.devlive.sdk.platform.google.model; + +import lombok.Getter; + +public enum GenerativeModel +{ + GEMINI_PRO("gemini-pro"), + GEMINI_1_0_PRO("gemini-1.0-pro"), + GEMINI_1_5_PRO_LATEST("gemini-1.5-pro-latest"), + GEMINI_1_5_FLASH_LATEST("gemini-1.5-flash-latest"); + + @Getter + private final String name; + + GenerativeModel(String name) + { + this.name = name; + } +} diff --git a/src/main/java/org/devlive/sdk/platform/google/model/RoleModel.java b/src/main/java/org/devlive/sdk/platform/google/model/RoleModel.java new file mode 100644 index 0000000..0f6fa8c --- /dev/null +++ b/src/main/java/org/devlive/sdk/platform/google/model/RoleModel.java @@ -0,0 +1,17 @@ +package org.devlive.sdk.platform.google.model; + +import lombok.Getter; + +public enum RoleModel +{ + USER("user"), + MODEL("model"); + + @Getter + private final String value; + + RoleModel(String value) + { + this.value = value; + } +} diff --git a/src/main/java/org/devlive/sdk/platform/google/model/VersionModel.java b/src/main/java/org/devlive/sdk/platform/google/model/VersionModel.java new file mode 100644 index 0000000..4eeda6d --- /dev/null +++ b/src/main/java/org/devlive/sdk/platform/google/model/VersionModel.java @@ -0,0 +1,16 @@ +package org.devlive.sdk.platform.google.model; + +import lombok.Getter; + +public enum VersionModel +{ + V1BETA("v1beta"); + + @Getter + private final String version; + + VersionModel(String version) + { + this.version = version; + } +} diff --git a/src/main/java/org/devlive/sdk/platform/google/response/CandidateResponse.java b/src/main/java/org/devlive/sdk/platform/google/response/CandidateResponse.java new file mode 100644 index 0000000..5a0fe78 --- /dev/null +++ b/src/main/java/org/devlive/sdk/platform/google/response/CandidateResponse.java @@ -0,0 +1,33 @@ +package org.devlive.sdk.platform.google.response; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.util.List; + +@Data +@ToString +@NoArgsConstructor +@AllArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +public class CandidateResponse +{ + @JsonProperty(value = "content") + private ContentResponse content; + + @JsonProperty(value = "finishReason") + private String finishReason; + + @JsonProperty(value = "index") + private Integer index; + + @JsonProperty(value = "safetyRatings") + private List safetyRatings; + + @JsonProperty(value = "citationMetadata") + private CitationMetadataResponse metadata; +} diff --git a/src/main/java/org/devlive/sdk/platform/google/response/ChatResponse.java b/src/main/java/org/devlive/sdk/platform/google/response/ChatResponse.java new file mode 100644 index 0000000..a99fb50 --- /dev/null +++ b/src/main/java/org/devlive/sdk/platform/google/response/ChatResponse.java @@ -0,0 +1,24 @@ +package org.devlive.sdk.platform.google.response; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.util.List; + +@Data +@ToString +@NoArgsConstructor +@AllArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +public class ChatResponse +{ + @JsonProperty(value = "candidates") + private List candidates; + + @JsonProperty(value = "usageMetadata") + private UsageResponse usage; +} diff --git a/src/main/java/org/devlive/sdk/platform/google/response/CitationMetadataResponse.java b/src/main/java/org/devlive/sdk/platform/google/response/CitationMetadataResponse.java new file mode 100644 index 0000000..f7a3e7d --- /dev/null +++ b/src/main/java/org/devlive/sdk/platform/google/response/CitationMetadataResponse.java @@ -0,0 +1,21 @@ +package org.devlive.sdk.platform.google.response; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.util.List; + +@Data +@ToString +@NoArgsConstructor +@AllArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +public class CitationMetadataResponse +{ + @JsonProperty(value = "citationSources") + List sources; +} diff --git a/src/main/java/org/devlive/sdk/platform/google/response/CitationSourceResponse.java b/src/main/java/org/devlive/sdk/platform/google/response/CitationSourceResponse.java new file mode 100644 index 0000000..b62b252 --- /dev/null +++ b/src/main/java/org/devlive/sdk/platform/google/response/CitationSourceResponse.java @@ -0,0 +1,28 @@ +package org.devlive.sdk.platform.google.response; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; + +@Data +@ToString +@NoArgsConstructor +@AllArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +public class CitationSourceResponse +{ + @JsonProperty(value = "startIndex") + private Integer start; + + @JsonProperty(value = "endIndex") + private Integer end; + + @JsonProperty(value = "uri") + private String uri; + + @JsonProperty(value = "license") + private String license; +} diff --git a/src/main/java/org/devlive/sdk/platform/google/response/ContentResponse.java b/src/main/java/org/devlive/sdk/platform/google/response/ContentResponse.java new file mode 100644 index 0000000..f7201a3 --- /dev/null +++ b/src/main/java/org/devlive/sdk/platform/google/response/ContentResponse.java @@ -0,0 +1,25 @@ +package org.devlive.sdk.platform.google.response; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; +import org.devlive.sdk.platform.google.entity.PartEntity; + +import java.util.List; + +@Data +@ToString +@NoArgsConstructor +@AllArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +public class ContentResponse +{ + @JsonProperty(value = "parts") + private List parts; + + @JsonProperty(value = "role") + private String role; +} diff --git a/src/main/java/org/devlive/sdk/platform/google/response/SafetyRatingResponse.java b/src/main/java/org/devlive/sdk/platform/google/response/SafetyRatingResponse.java new file mode 100644 index 0000000..0bd6b4d --- /dev/null +++ b/src/main/java/org/devlive/sdk/platform/google/response/SafetyRatingResponse.java @@ -0,0 +1,22 @@ +package org.devlive.sdk.platform.google.response; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; + +@Data +@ToString +@NoArgsConstructor +@AllArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +public class SafetyRatingResponse +{ + @JsonProperty(value = "category") + private String category; + + @JsonProperty(value = "probability") + private String probability; +} diff --git a/src/main/java/org/devlive/sdk/platform/google/response/UsageResponse.java b/src/main/java/org/devlive/sdk/platform/google/response/UsageResponse.java new file mode 100644 index 0000000..131820b --- /dev/null +++ b/src/main/java/org/devlive/sdk/platform/google/response/UsageResponse.java @@ -0,0 +1,25 @@ +package org.devlive.sdk.platform.google.response; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; + +@Data +@ToString +@NoArgsConstructor +@AllArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +public class UsageResponse +{ + @JsonProperty(value = "promptTokenCount") + private Integer promptTokenCount; + + @JsonProperty(value = "candidatesTokenCount") + private Integer candidatesTokenCount; + + @JsonProperty(value = "totalTokenCount") + private Integer totalTokenCount; +} diff --git a/src/test/java/org/devlive/sdk/ResourceUtils.java b/src/test/java/org/devlive/sdk/ResourceUtils.java new file mode 100644 index 0000000..fc05b5f --- /dev/null +++ b/src/test/java/org/devlive/sdk/ResourceUtils.java @@ -0,0 +1,29 @@ +package org.devlive.sdk; + +import lombok.extern.slf4j.Slf4j; + +import java.util.Properties; + +@Slf4j +public class ResourceUtils +{ + /** + * Get value from application.properties + * + * @param token key + * @return value + */ + public static String getValue(String token) + { + Properties properties = new Properties(); + try { + properties.load(ResourceUtils.class + .getClassLoader() + .getResourceAsStream("application.properties")); + } + catch (Exception e) { + log.error("Load application.properties error", e); + } + return properties.getProperty(token); + } +} diff --git a/src/test/java/org/devlive/sdk/ResourceUtilsTest.java b/src/test/java/org/devlive/sdk/ResourceUtilsTest.java new file mode 100644 index 0000000..f833be0 --- /dev/null +++ b/src/test/java/org/devlive/sdk/ResourceUtilsTest.java @@ -0,0 +1,15 @@ +package org.devlive.sdk; + +import org.junit.Test; + +import static org.junit.Assert.assertNull; + +public class ResourceUtilsTest +{ + @Test + public void testGetValue() + { + String value = ResourceUtils.getValue("key"); + assertNull(value); + } +} diff --git a/src/test/java/org/devlive/sdk/common/utils/ValidateUtilsTest.java b/src/test/java/org/devlive/sdk/common/utils/ValidateUtilsTest.java new file mode 100644 index 0000000..db136d5 --- /dev/null +++ b/src/test/java/org/devlive/sdk/common/utils/ValidateUtilsTest.java @@ -0,0 +1,16 @@ +package org.devlive.sdk.common.utils; + +import org.devlive.sdk.common.exception.ParamException; +import org.junit.Assert; +import org.junit.Test; + +public class ValidateUtilsTest +{ + + @Test + public void testValidateHost() + { + // host is empty + Assert.assertThrows(ParamException.class, () -> ValidateUtils.validateHost("", null)); + } +} diff --git a/src/test/java/org/devlive/sdk/openai/AzureOpenAiClientTest.java b/src/test/java/org/devlive/sdk/openai/AzureOpenAiClientTest.java index 9d08ba9..c23f594 100644 --- a/src/test/java/org/devlive/sdk/openai/AzureOpenAiClientTest.java +++ b/src/test/java/org/devlive/sdk/openai/AzureOpenAiClientTest.java @@ -4,7 +4,7 @@ import org.devlive.sdk.openai.entity.ChatEntity; import org.devlive.sdk.openai.entity.CompletionEntity; import org.devlive.sdk.openai.entity.MessageEntity; -import org.devlive.sdk.openai.exception.RequestException; +import org.devlive.sdk.common.exception.RequestException; import org.devlive.sdk.openai.model.CompletionModel; import org.devlive.sdk.openai.model.ProviderModel; import org.junit.Assert; diff --git a/src/test/java/org/devlive/sdk/openai/FineTuningTest.java b/src/test/java/org/devlive/sdk/openai/FineTuningTest.java index 88759d8..d67fc5b 100644 --- a/src/test/java/org/devlive/sdk/openai/FineTuningTest.java +++ b/src/test/java/org/devlive/sdk/openai/FineTuningTest.java @@ -1,7 +1,7 @@ package org.devlive.sdk.openai; import org.devlive.sdk.openai.entity.FineTuningEntity; -import org.devlive.sdk.openai.exception.RequestException; +import org.devlive.sdk.common.exception.RequestException; import org.junit.Assert; import org.junit.Before; import org.junit.Test; diff --git a/src/test/java/org/devlive/sdk/openai/OpenAiClientTest.java b/src/test/java/org/devlive/sdk/openai/OpenAiClientTest.java index 2d9413b..cd19897 100644 --- a/src/test/java/org/devlive/sdk/openai/OpenAiClientTest.java +++ b/src/test/java/org/devlive/sdk/openai/OpenAiClientTest.java @@ -11,8 +11,8 @@ import org.devlive.sdk.openai.entity.MessageEntity; import org.devlive.sdk.openai.entity.ModerationEntity; import org.devlive.sdk.openai.entity.UserKeyEntity; -import org.devlive.sdk.openai.exception.AuthorizedException; -import org.devlive.sdk.openai.exception.RequestException; +import org.devlive.sdk.common.exception.AuthorizedException; +import org.devlive.sdk.common.exception.RequestException; import org.devlive.sdk.openai.model.CompletionModel; import org.devlive.sdk.openai.model.EditModel; import org.junit.Assert; diff --git a/src/test/java/org/devlive/sdk/openai/StreamClientTest.java b/src/test/java/org/devlive/sdk/openai/StreamClientTest.java index a9d74fd..6da8469 100644 --- a/src/test/java/org/devlive/sdk/openai/StreamClientTest.java +++ b/src/test/java/org/devlive/sdk/openai/StreamClientTest.java @@ -2,10 +2,11 @@ import com.google.common.collect.Lists; import lombok.extern.slf4j.Slf4j; +import org.devlive.sdk.ResourceUtils; +import org.devlive.sdk.common.listener.ConsoleEventSourceListener; import org.devlive.sdk.openai.entity.ChatEntity; import org.devlive.sdk.openai.entity.CompletionEntity; import org.devlive.sdk.openai.entity.MessageEntity; -import org.devlive.sdk.openai.listener.ConsoleEventSourceListener; import org.junit.Before; import org.junit.Test; @@ -26,7 +27,7 @@ public void before() .countDownLatch(countDownLatch) .build(); client = OpenAiClient.builder() - .apiKey(System.getProperty("openai.token")) + .apiKey(ResourceUtils.getValue("openai.token")) .listener(listener) .build(); } diff --git a/src/test/java/org/devlive/sdk/openai/entity/AudioEntityTest.java b/src/test/java/org/devlive/sdk/openai/entity/AudioEntityTest.java index 97933de..e5f65ee 100644 --- a/src/test/java/org/devlive/sdk/openai/entity/AudioEntityTest.java +++ b/src/test/java/org/devlive/sdk/openai/entity/AudioEntityTest.java @@ -1,6 +1,6 @@ package org.devlive.sdk.openai.entity; -import org.devlive.sdk.openai.exception.ParamException; +import org.devlive.sdk.common.exception.ParamException; import org.junit.Assert; import org.junit.Test; diff --git a/src/test/java/org/devlive/sdk/openai/entity/ChatEntityTest.java b/src/test/java/org/devlive/sdk/openai/entity/ChatEntityTest.java index d7eac80..76e6910 100644 --- a/src/test/java/org/devlive/sdk/openai/entity/ChatEntityTest.java +++ b/src/test/java/org/devlive/sdk/openai/entity/ChatEntityTest.java @@ -1,6 +1,6 @@ package org.devlive.sdk.openai.entity; -import org.devlive.sdk.openai.exception.ParamException; +import org.devlive.sdk.common.exception.ParamException; import org.devlive.sdk.openai.model.CompletionModel; import org.junit.Assert; import org.junit.Test; diff --git a/src/test/java/org/devlive/sdk/openai/entity/CompletionEntityTest.java b/src/test/java/org/devlive/sdk/openai/entity/CompletionEntityTest.java index c7df293..c478135 100644 --- a/src/test/java/org/devlive/sdk/openai/entity/CompletionEntityTest.java +++ b/src/test/java/org/devlive/sdk/openai/entity/CompletionEntityTest.java @@ -1,6 +1,6 @@ package org.devlive.sdk.openai.entity; -import org.devlive.sdk.openai.exception.ParamException; +import org.devlive.sdk.common.exception.ParamException; import org.devlive.sdk.openai.model.CompletionModel; import org.junit.Assert; import org.junit.Test; diff --git a/src/test/java/org/devlive/sdk/openai/entity/EditEntityTest.java b/src/test/java/org/devlive/sdk/openai/entity/EditEntityTest.java index dda241d..1c0ebdd 100644 --- a/src/test/java/org/devlive/sdk/openai/entity/EditEntityTest.java +++ b/src/test/java/org/devlive/sdk/openai/entity/EditEntityTest.java @@ -1,6 +1,6 @@ package org.devlive.sdk.openai.entity; -import org.devlive.sdk.openai.exception.ParamException; +import org.devlive.sdk.common.exception.ParamException; import org.junit.Assert; import org.junit.Test; diff --git a/src/test/java/org/devlive/sdk/openai/entity/EmbeddingEntityTest.java b/src/test/java/org/devlive/sdk/openai/entity/EmbeddingEntityTest.java index 52b08b3..a039b60 100644 --- a/src/test/java/org/devlive/sdk/openai/entity/EmbeddingEntityTest.java +++ b/src/test/java/org/devlive/sdk/openai/entity/EmbeddingEntityTest.java @@ -1,6 +1,6 @@ package org.devlive.sdk.openai.entity; -import org.devlive.sdk.openai.exception.ParamException; +import org.devlive.sdk.common.exception.ParamException; import org.devlive.sdk.openai.model.CompletionModel; import org.junit.Assert; import org.junit.Test; diff --git a/src/test/java/org/devlive/sdk/openai/entity/FileEntityTest.java b/src/test/java/org/devlive/sdk/openai/entity/FileEntityTest.java index d7eeaa2..fee9a51 100644 --- a/src/test/java/org/devlive/sdk/openai/entity/FileEntityTest.java +++ b/src/test/java/org/devlive/sdk/openai/entity/FileEntityTest.java @@ -1,6 +1,6 @@ package org.devlive.sdk.openai.entity; -import org.devlive.sdk.openai.exception.ParamException; +import org.devlive.sdk.common.exception.ParamException; import org.junit.Assert; import org.junit.Test; diff --git a/src/test/java/org/devlive/sdk/openai/entity/FineTuningEntityTest.java b/src/test/java/org/devlive/sdk/openai/entity/FineTuningEntityTest.java index ef95fcb..0d741ed 100644 --- a/src/test/java/org/devlive/sdk/openai/entity/FineTuningEntityTest.java +++ b/src/test/java/org/devlive/sdk/openai/entity/FineTuningEntityTest.java @@ -1,6 +1,6 @@ package org.devlive.sdk.openai.entity; -import org.devlive.sdk.openai.exception.ParamException; +import org.devlive.sdk.common.exception.ParamException; import org.junit.Assert; import org.junit.Test; diff --git a/src/test/java/org/devlive/sdk/openai/entity/ImageEntityTest.java b/src/test/java/org/devlive/sdk/openai/entity/ImageEntityTest.java index c694471..1c5772b 100644 --- a/src/test/java/org/devlive/sdk/openai/entity/ImageEntityTest.java +++ b/src/test/java/org/devlive/sdk/openai/entity/ImageEntityTest.java @@ -1,6 +1,6 @@ package org.devlive.sdk.openai.entity; -import org.devlive.sdk.openai.exception.ParamException; +import org.devlive.sdk.common.exception.ParamException; import org.devlive.sdk.openai.model.ImageSizeModel; import org.junit.Assert; import org.junit.Test; diff --git a/src/test/java/org/devlive/sdk/openai/entity/ModerationEntityTest.java b/src/test/java/org/devlive/sdk/openai/entity/ModerationEntityTest.java index e1e5d4b..b8ff9b5 100644 --- a/src/test/java/org/devlive/sdk/openai/entity/ModerationEntityTest.java +++ b/src/test/java/org/devlive/sdk/openai/entity/ModerationEntityTest.java @@ -1,7 +1,7 @@ package org.devlive.sdk.openai.entity; import com.google.common.collect.Lists; -import org.devlive.sdk.openai.exception.ParamException; +import org.devlive.sdk.common.exception.ParamException; import org.devlive.sdk.openai.model.ModerationModel; import org.junit.Assert; import org.junit.Test; diff --git a/src/test/java/org/devlive/sdk/openai/entity/beta/AssistantsEntityTest.java b/src/test/java/org/devlive/sdk/openai/entity/beta/AssistantsEntityTest.java index 58a65d7..f9dc805 100644 --- a/src/test/java/org/devlive/sdk/openai/entity/beta/AssistantsEntityTest.java +++ b/src/test/java/org/devlive/sdk/openai/entity/beta/AssistantsEntityTest.java @@ -1,6 +1,6 @@ package org.devlive.sdk.openai.entity.beta; -import org.devlive.sdk.openai.exception.ParamException; +import org.devlive.sdk.common.exception.ParamException; import org.devlive.sdk.openai.model.CompletionModel; import org.junit.Assert; import org.junit.Test; diff --git a/src/test/java/org/devlive/sdk/openai/listener/HttpServletEventSourceListenerTest.java b/src/test/java/org/devlive/sdk/openai/listener/HttpServletEventSourceListenerTest.java index 39ebb67..f8d19a9 100644 --- a/src/test/java/org/devlive/sdk/openai/listener/HttpServletEventSourceListenerTest.java +++ b/src/test/java/org/devlive/sdk/openai/listener/HttpServletEventSourceListenerTest.java @@ -3,6 +3,7 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpSession; +import org.devlive.sdk.common.listener.HttpServletEventSourceListener; import org.devlive.sdk.openai.OpenAiClient; import org.devlive.sdk.openai.entity.CompletionEntity; import org.junit.Test; diff --git a/src/test/java/org/devlive/sdk/platform/google/GoogleClientTest.java b/src/test/java/org/devlive/sdk/platform/google/GoogleClientTest.java new file mode 100644 index 0000000..483ddef --- /dev/null +++ b/src/test/java/org/devlive/sdk/platform/google/GoogleClientTest.java @@ -0,0 +1,127 @@ +package org.devlive.sdk.platform.google; + +import com.google.common.collect.Lists; +import lombok.extern.slf4j.Slf4j; +import org.devlive.sdk.ResourceUtils; +import org.devlive.sdk.platform.google.entity.ChatEntity; +import org.devlive.sdk.platform.google.entity.ObjectEntity; +import org.devlive.sdk.platform.google.entity.PartEntity; +import org.devlive.sdk.platform.google.model.RoleModel; +import org.devlive.sdk.platform.google.response.ChatResponse; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.List; + +@Slf4j +public class GoogleClientTest +{ + private GoogleClient client; + private String token; + + @Before + public void before() + { + token = ResourceUtils.getValue("google.token"); + client = GoogleClient.builder() + .apiKey(token) + .build(); + } + + @Test + public void testBuilder() + { + Assert.assertNotNull(client); + } + + @Test + public void testCreateChat() + { + PartEntity part = PartEntity.builder() + .text("Hello, Open AI Java SDK!") + .build(); + ObjectEntity object = ObjectEntity.builder() + .parts(Lists.newArrayList(part)) + .build(); + ChatEntity chat = ChatEntity.builder() + .contents(Lists.newArrayList(object)) + .build(); + + Assert.assertNotNull(client.createChatCompletions(chat)); + } + + @Test + public void testAutoClose() + { + try (GoogleClient client = GoogleClient.builder() + .apiKey(token) + .build()) { + PartEntity part = PartEntity.builder() + .text("Hello, Open AI Java SDK!") + .build(); + ObjectEntity object = ObjectEntity.builder() + .parts(Lists.newArrayList(part)) + .build(); + ChatEntity chat = ChatEntity.builder() + .contents(Lists.newArrayList(object)) + .build(); + + ChatResponse response = client.createChatCompletions(chat); + response.getCandidates() + .forEach(item -> item.getContent() + .getParts() + .forEach(value -> log.info(value.getText()))); + + Assert.assertNotNull(response); + } + } + + @Test + public void testContinuousChat() + { + List contents = Lists.newArrayList(); + PartEntity part = PartEntity.builder() + .text("你好,我叫小明") + .build(); + ObjectEntity object = ObjectEntity.builder() + .parts(Lists.newArrayList(part)) + .build(); + contents.add(object); + ChatEntity chat = ChatEntity.builder() + .contents(contents) + .build(); + ChatResponse response = client.createChatCompletions(chat); + print(response, contents); + + ObjectEntity newObject = ObjectEntity.builder() + .parts(Lists.newArrayList(PartEntity.builder() + .text("我刚刚说了什么") + .build())) + .build(); + contents.add(newObject); + ChatEntity newChat = ChatEntity.builder() + .contents(contents) + .build(); + ChatResponse newResponse = client.createChatCompletions(newChat); + print(response, contents); + Assert.assertNotNull(newResponse.getCandidates()); + } + + private void print(ChatResponse response, List contents) + { + response.getCandidates() + .forEach(item -> item.getContent() + .getParts() + .forEach(value -> { + log.info(value.getText()); + + contents.add(ObjectEntity.builder() + .role(RoleModel.MODEL) + .parts(Lists.newArrayList(PartEntity.builder() + .text(value.getText()) + .build())) + .build()); + })); + } +} diff --git a/src/test/java/org/devlive/sdk/platform/google/GoogleStreamClientTest.java b/src/test/java/org/devlive/sdk/platform/google/GoogleStreamClientTest.java new file mode 100644 index 0000000..f3019fc --- /dev/null +++ b/src/test/java/org/devlive/sdk/platform/google/GoogleStreamClientTest.java @@ -0,0 +1,59 @@ +package org.devlive.sdk.platform.google; + +import com.google.common.collect.Lists; +import lombok.extern.slf4j.Slf4j; +import org.devlive.sdk.ResourceUtils; +import org.devlive.sdk.common.listener.ConsoleEventSourceListener; +import org.devlive.sdk.platform.google.entity.ChatEntity; +import org.devlive.sdk.platform.google.entity.ObjectEntity; +import org.devlive.sdk.platform.google.entity.PartEntity; +import org.junit.Before; +import org.junit.Test; + +import java.util.List; +import java.util.concurrent.CountDownLatch; + +@Slf4j +public class GoogleStreamClientTest +{ + private GoogleClient client; + private CountDownLatch countDownLatch; + private String token; + + @Before + public void before() + { + countDownLatch = new CountDownLatch(1); + ConsoleEventSourceListener listener = ConsoleEventSourceListener.builder() + .countDownLatch(countDownLatch) + .build(); + token = ResourceUtils.getValue("google.token"); + client = GoogleClient.builder() + .apiKey(token) + .listener(listener) + .build(); + } + + @Test + public void testCreateChat() + { + List contents = Lists.newArrayList(); + PartEntity part = PartEntity.builder() + .text("帮我写一万字的作文") + .build(); + ObjectEntity object = ObjectEntity.builder() + .parts(Lists.newArrayList(part)) + .build(); + contents.add(object); + ChatEntity chat = ChatEntity.builder() + .contents(contents) + .build(); + client.createChatCompletions(chat); + try { + countDownLatch.await(); + } + catch (InterruptedException e) { + log.error("Interrupted while waiting", e); + } + } +} diff --git a/src/test/java/org/devlive/sdk/platform/google/entity/ChatEntityTest.java b/src/test/java/org/devlive/sdk/platform/google/entity/ChatEntityTest.java new file mode 100644 index 0000000..2b1ad15 --- /dev/null +++ b/src/test/java/org/devlive/sdk/platform/google/entity/ChatEntityTest.java @@ -0,0 +1,36 @@ +package org.devlive.sdk.platform.google.entity; + +import com.google.common.collect.Lists; +import org.devlive.sdk.common.exception.ParamException; +import org.junit.Assert; +import org.junit.Test; + +public class ChatEntityTest +{ + @Test + public void testContents() + { + // contents is null + Assert.assertThrows(ParamException.class, () -> ChatEntity.builder() + .contents(null) + .build()); + + // contents is empty + Assert.assertThrows(ParamException.class, () -> ChatEntity.builder() + .contents(Lists.newArrayList()) + .build()); + + // contents is not empty + PartEntity part = PartEntity.builder() + .text("Hello, Open AI Java SDK!") + .build(); + ObjectEntity object = ObjectEntity.builder() + .parts(Lists.newArrayList(part)) + .build(); + Assert.assertEquals(1, ChatEntity.builder() + .contents(Lists.newArrayList(object)) + .build() + .getContents() + .size()); + } +} diff --git a/src/test/java/org/devlive/sdk/platform/google/entity/ObjectEntityTest.java b/src/test/java/org/devlive/sdk/platform/google/entity/ObjectEntityTest.java new file mode 100644 index 0000000..069345a --- /dev/null +++ b/src/test/java/org/devlive/sdk/platform/google/entity/ObjectEntityTest.java @@ -0,0 +1,33 @@ +package org.devlive.sdk.platform.google.entity; + +import com.google.common.collect.Lists; +import org.devlive.sdk.common.exception.ParamException; +import org.junit.Assert; +import org.junit.Test; + +public class ObjectEntityTest +{ + @Test + public void testParts() + { + // parts is null + Assert.assertThrows(ParamException.class, () -> ObjectEntity.builder() + .parts(null) + .build()); + + // parts is empty + Assert.assertThrows(ParamException.class, () -> ObjectEntity.builder() + .parts(Lists.newArrayList()) + .build()); + + // parts is not empty + PartEntity entity = PartEntity.builder() + .text("Hello, Open AI Java SDK!") + .build(); + Assert.assertEquals(1, ObjectEntity.builder() + .parts(Lists.newArrayList(entity)) + .build() + .getParts() + .size()); + } +} diff --git a/src/test/java/org/devlive/sdk/platform/google/entity/PartEntityTest.java b/src/test/java/org/devlive/sdk/platform/google/entity/PartEntityTest.java new file mode 100644 index 0000000..5f25660 --- /dev/null +++ b/src/test/java/org/devlive/sdk/platform/google/entity/PartEntityTest.java @@ -0,0 +1,30 @@ +package org.devlive.sdk.platform.google.entity; + +import org.devlive.sdk.common.exception.ParamException; +import org.junit.Assert; +import org.junit.Test; + +public class PartEntityTest +{ + private String text = "Hello, Open AI Java SDK!"; + + @Test + public void testText() + { + // test is empty + Assert.assertThrows(ParamException.class, () -> PartEntity.builder() + .text("") + .build()); + + // text is null + Assert.assertThrows(ParamException.class, () -> PartEntity.builder() + .text(null) + .build()); + + // test not blank + Assert.assertEquals(text, PartEntity.builder() + .text(text) + .build() + .getText()); + } +}