From 640ad2d2e87c408a0b1704094fcaae8ab91895e6 Mon Sep 17 00:00:00 2001 From: guorutao <285012> Date: Sun, 12 Nov 2023 23:00:02 +0800 Subject: [PATCH] =?UTF-8?q?feature=201.1.2=20=E6=96=B0=E7=9A=84api?= =?UTF-8?q?=E5=BC=80=E5=8F=91=EF=BC=9Atts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fine_tune_test_file.json1 | 10 + .../java/com/unfbx/chatgpt/OpenAiApi.java | 19 +- .../java/com/unfbx/chatgpt/OpenAiClient.java | 41 ++- .../com/unfbx/chatgpt/OpenAiStreamClient.java | 21 -- .../unfbx/chatgpt/entity/Tts/TtsVoice.java | 12 +- .../entity/fineTune/job/FineTuneError.java | 19 ++ .../fineTune/job/FineTuneJobResponse.java | 15 +- .../unfbx/chatgpt/entity/images/Image.java | 3 +- .../chatgpt/OpenAiClientFunctionTest.java | 4 +- .../com/unfbx/chatgpt/OpenAiClientTest.java | 28 +- .../unfbx/chatgpt/OpenAiStreamClientTest.java | 4 +- .../java/com/unfbx/chatgpt/PluginTest.java | 4 +- .../v1_1_2/ConsoleEventSourceListenerV3.java | 92 ++++++ .../chatgpt/v1_1_2/OpenAiClientTest.java | 297 +++++++++++++++++- 14 files changed, 469 insertions(+), 100 deletions(-) create mode 100644 fine_tune_test_file.json1 create mode 100644 src/main/java/com/unfbx/chatgpt/entity/fineTune/job/FineTuneError.java create mode 100644 src/test/java/com/unfbx/chatgpt/v1_1_2/ConsoleEventSourceListenerV3.java diff --git a/fine_tune_test_file.json1 b/fine_tune_test_file.json1 new file mode 100644 index 0000000..42f8e6e --- /dev/null +++ b/fine_tune_test_file.json1 @@ -0,0 +1,10 @@ +{"messages": [{"role": "system", "content": "OnBot是一个聊天机器人。"}, {"role": "user", "content": "chagpt-java好用么?"}, {"role": "assistant", "content": "还行吧。"}]} +{"messages": [{"role": "system", "content": "OnBot是一个聊天机器人。"}, {"role": "user", "content": "chagpt-java最新版本是什么?"}, {"role": "assistant", "content": "1.1.2版本"}]} +{"messages": [{"role": "system", "content": "OnBot是一个聊天机器人。"}, {"role": "user", "content": "chagpt-java支持流式输出么?"}, {"role": "assistant", "content": "最新版支持流式输出"}]} +{"messages": [{"role": "system", "content": "OnBot是一个聊天机器人。"}, {"role": "user", "content": "chagpt-java的作者是谁?"}, {"role": "assistant", "content": "作者是Unfbx,个人网站:https://www.unfbx.com"}]} +{"messages": [{"role": "system", "content": "OnBot是一个聊天机器人。"}, {"role": "user", "content": "Unfbx是chagpt-java的作者,微信公众号是什么?"}, {"role": "assistant", "content": "程序员的黑洞。"}]} +{"messages": [{"role": "system", "content": "OnBot是一个聊天机器人。"}, {"role": "user", "content": "Unfbx是chagpt-java的作者,个人博客地址是什么?"}, {"role": "assistant", "content": "https://www.unfbx.com"}]} +{"messages": [{"role": "system", "content": "OnBot是一个聊天机器人。"}, {"role": "user", "content": "Unfbx是chagpt-java的作者,chagpt-java官方地址是什么?"}, {"role": "assistant", "content": "https://chatgpt-java.unfbx.com"}]} +{"messages": [{"role": "system", "content": "OnBot是一个聊天机器人。"}, {"role": "user", "content": "Unfbx是chagpt-java的作者,chagpt-java的github地址是什么?"}, {"role": "assistant", "content": "https://github.com/Grt1228/chatgpt-java"}]} +{"messages": [{"role": "system", "content": "OnBot是一个聊天机器人。"}, {"role": "user", "content": "Unfbx是chagpt-java的作者,chagpt-java的简单介绍。"}, {"role": "assistant", "content": "OpenAI官方Api的Java SDK,可以快速接入项目使用。目前支持OpenAI官方全部接口,**同时支持Tokens计算。参考文档:[Tokens_README.md](https://github.com/Grt1228/chatgpt-java/blob/main/Tokens_README.md)**"}]} +{"messages": [{"role": "system", "content": "OnBot是一个聊天机器人。"}, {"role": "user", "content": "OpenAi的官方Java Sdk 是什么?"}, {"role": "assistant", "content": "chagpt-java,github地址:https://github.com/Grt1228/chatgpt-java"}]} diff --git a/src/main/java/com/unfbx/chatgpt/OpenAiApi.java b/src/main/java/com/unfbx/chatgpt/OpenAiApi.java index 24b5d49..627ae79 100644 --- a/src/main/java/com/unfbx/chatgpt/OpenAiApi.java +++ b/src/main/java/com/unfbx/chatgpt/OpenAiApi.java @@ -4,7 +4,6 @@ import com.unfbx.chatgpt.entity.billing.BillingUsage; import com.unfbx.chatgpt.entity.billing.CreditGrantsResponse; import com.unfbx.chatgpt.entity.billing.Subscription; -import com.unfbx.chatgpt.entity.chat.BaseChatCompletion; import com.unfbx.chatgpt.entity.chat.ChatCompletion; import com.unfbx.chatgpt.entity.chat.ChatCompletionResponse; import com.unfbx.chatgpt.entity.chat.ChatCompletionWithPicture; @@ -217,7 +216,7 @@ Single uploadFile(@Part MultipartBody.Part file, * 微调作业集合 * * @return Single OpenAiResponse FineTuneResponse - * @see #fineTuneJobs() + * @see #fineTuneJobs(String, Integer) */ @Deprecated @GET("v1/fine-tunes") @@ -248,7 +247,7 @@ Single uploadFile(@Part MultipartBody.Part file, * 微调作业事件列表 * * @return Single OpenAiResponse Event - * @see #fineTuneJobEvents(String fineTuneJobId) + * @see #fineTuneJobEvents(String, String, Integer) */ @Deprecated @GET("v1/fine-tunes/{fine_tune_id}/events") @@ -362,8 +361,10 @@ Single speechToTextTranslations(@Part MultipartBody.Part file, * * @param textToSpeech * @return + * @since 1.1.2 */ @POST("v1/audio/speech") + @Streaming Call textToSpeech(@Body TextToSpeech textToSpeech); @@ -380,11 +381,13 @@ Single speechToTextTranslations(@Part MultipartBody.Part file, /** * 微调job集合 * + * @param after 上一个分页请求中最后一个job id + * @param limit 每次查询数量 * @return FineTuneJobListResponse #FineTuneResponse * @since 1.1.2 */ @GET("v1/fine_tuning/jobs") - Single> fineTuneJobs(); + Single> fineTuneJobs(@Query("after") String after, @Query("limit") Integer limit); /** @@ -400,7 +403,7 @@ Single speechToTextTranslations(@Part MultipartBody.Part file, /** * 取消微调job * - * @param fineTuneJobId + * @param fineTuneJobId JobId * @return FineTuneJobResponse * @since 1.1.2 */ @@ -410,11 +413,13 @@ Single speechToTextTranslations(@Part MultipartBody.Part file, /** * 微调job事件列表 * - * @param fineTuneJobId + * @param fineTuneJobId JobId + * @param after 上一个分页请求中最后一个id,默认值:null + * @param limit 每次查询数量 默认值:20 * @return FineTuneJobListResponse #FineTuneResponse * @since 1.1.2 */ @GET("v1/fine_tuning/jobs/{fine_tuning_job_id}/events") - Single> fineTuneJobEvents(@Path("fine_tuning_job_id") String fineTuneJobId); + Single> fineTuneJobEvents(@Path("fine_tuning_job_id") String fineTuneJobId, @Query("after") String after, @Query("limit") Integer limit); } diff --git a/src/main/java/com/unfbx/chatgpt/OpenAiClient.java b/src/main/java/com/unfbx/chatgpt/OpenAiClient.java index 0663430..4fcbf39 100644 --- a/src/main/java/com/unfbx/chatgpt/OpenAiClient.java +++ b/src/main/java/com/unfbx/chatgpt/OpenAiClient.java @@ -60,6 +60,7 @@ import retrofit2.converter.jackson.JacksonConverterFactory; import java.io.IOException; +import java.io.InputStream; import java.time.LocalDate; import java.util.*; import java.util.concurrent.TimeUnit; @@ -586,7 +587,7 @@ public FineTuneResponse fineTune(String trainingFileId) { * 微调模型列表 * * @return FineTuneResponse list - * @see #fineTuneJobs() + * @see #fineTuneJobs(String, Integer) */ @Deprecated public List fineTunes() { @@ -625,7 +626,7 @@ public FineTuneResponse cancelFineTune(String fineTuneId) { * * @param fineTuneId 微调作业id * @return Event List - * @see #fineTuneJobEvents(String fineTuneJobId) + * @see #fineTuneJobEvents(String, String, Integer) */ @Deprecated public List fineTuneEvents(String fineTuneId) { @@ -933,18 +934,23 @@ public FineTuneJobResponse fineTuneJob(FineTuneJob fineTuneJob) { * @since 1.1.2 */ public FineTuneJobResponse fineTuneJob(String trainingFileId) { - FineTuneJob fineTuneJob = FineTuneJob.builder().trainingFile(trainingFileId).build(); + FineTuneJob fineTuneJob = FineTuneJob.builder() + .model(FineTuneJob.Model.GPT_3_5_TURBO_1106.getName()) + .trainingFile(trainingFileId) + .build(); return this.fineTuneJob(fineTuneJob); } /** - * 微调job列表 + * 微调job集合 * - * @return FineTuneJobListResponse #FineTuneJobResponse + * @param after 上一个分页请求中最后一个job id,默认值:null + * @param limit 每次查询数量 默认值:20 + * @return FineTuneJobListResponse #FineTuneResponse * @since 1.1.2 */ - public FineTuneJobListResponse fineTuneJobs() { - Single> fineTuneJobs = this.openAiApi.fineTuneJobs(); + public FineTuneJobListResponse fineTuneJobs(String after, Integer limit) { + Single> fineTuneJobs = this.openAiApi.fineTuneJobs(after, limit); return fineTuneJobs.blockingGet(); } @@ -976,29 +982,20 @@ public FineTuneJobResponse cancelFineTuneJob(String fineTuneJobId) { * 微调作业事件列表 * * @param fineTuneJobId 微调job id + * @param after 上一个分页请求中最后一个id,默认值:null + * @param limit 每次查询数量 默认值:20 * @return Event List * @since 1.1.2 */ - public FineTuneJobListResponse fineTuneJobEvents(String fineTuneJobId) { - Single> events = this.openAiApi.fineTuneJobEvents(fineTuneJobId); + public FineTuneJobListResponse fineTuneJobEvents(String fineTuneJobId, String after, Integer limit) { + Single> events = this.openAiApi.fineTuneJobEvents(fineTuneJobId, after, limit); return events.blockingGet(); } - public void textToSpeech(TextToSpeech textToSpeech) { + public void textToSpeech(TextToSpeech textToSpeech, Callback callback) { Call responseBody = this.openAiApi.textToSpeech(textToSpeech); - responseBody.enqueue(new Callback() { - @SneakyThrows - @Override - public void onResponse(Call call, Response response) { - System.out.println("--------------------->" + response.body()); - } - - @Override - public void onFailure(Call call, Throwable t) { - - } - }); + responseBody.enqueue(callback); } public static final class Builder { diff --git a/src/main/java/com/unfbx/chatgpt/OpenAiStreamClient.java b/src/main/java/com/unfbx/chatgpt/OpenAiStreamClient.java index f1c4aef..871683a 100644 --- a/src/main/java/com/unfbx/chatgpt/OpenAiStreamClient.java +++ b/src/main/java/com/unfbx/chatgpt/OpenAiStreamClient.java @@ -156,27 +156,6 @@ private OkHttpClient okHttpClient() { .build(); } - - public void textToSpeed(TextToSpeech textToSpeech, EventSourceListener eventSourceListener) { - try { - EventSource.Factory factory = EventSources.createFactory(this.okHttpClient); - ObjectMapper mapper = new ObjectMapper(); - String requestBody = mapper.writeValueAsString(textToSpeech); - Request request = new Request.Builder() - .url(this.apiHost + "v1/audio/speech") - .post(RequestBody.create(MediaType.parse(ContentType.JSON.getValue()), requestBody)) - .build(); - //创建事件 - EventSource eventSource = factory.newEventSource(request, eventSourceListener); - } catch (JsonProcessingException e) { - log.error("请求参数解析异常:{}", e); - e.printStackTrace(); - } catch (Exception e) { - log.error("请求参数解析异常:{}", e); - e.printStackTrace(); - } - } - /** * 问答接口 stream 形式 * diff --git a/src/main/java/com/unfbx/chatgpt/entity/Tts/TtsVoice.java b/src/main/java/com/unfbx/chatgpt/entity/Tts/TtsVoice.java index 318ccd5..0ab25da 100644 --- a/src/main/java/com/unfbx/chatgpt/entity/Tts/TtsVoice.java +++ b/src/main/java/com/unfbx/chatgpt/entity/Tts/TtsVoice.java @@ -11,12 +11,12 @@ @AllArgsConstructor public enum TtsVoice { - ALLOY("mp3"), - ECHO("opus"), - FABLE("aac"), - ONYX("flac"), - NOVA("flac"), - SHIMMER("flac"), + ALLOY("alloy"), + ECHO("echo"), + FABLE("fable"), + ONYX("onyx"), + NOVA("nova"), + SHIMMER("shimmer"), ; private final String name; diff --git a/src/main/java/com/unfbx/chatgpt/entity/fineTune/job/FineTuneError.java b/src/main/java/com/unfbx/chatgpt/entity/fineTune/job/FineTuneError.java new file mode 100644 index 0000000..c47ed04 --- /dev/null +++ b/src/main/java/com/unfbx/chatgpt/entity/fineTune/job/FineTuneError.java @@ -0,0 +1,19 @@ +package com.unfbx.chatgpt.entity.fineTune.job; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Data; + +/** + * 描述: + * + * @author https://www.unfbx.com + * @since 1.1.2 + * 2023-11-12 + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class FineTuneError { + private String message; + private String param; + private String code; +} diff --git a/src/main/java/com/unfbx/chatgpt/entity/fineTune/job/FineTuneJobResponse.java b/src/main/java/com/unfbx/chatgpt/entity/fineTune/job/FineTuneJobResponse.java index 238724d..b2741b6 100644 --- a/src/main/java/com/unfbx/chatgpt/entity/fineTune/job/FineTuneJobResponse.java +++ b/src/main/java/com/unfbx/chatgpt/entity/fineTune/job/FineTuneJobResponse.java @@ -2,12 +2,18 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; -import com.unfbx.chatgpt.entity.common.OpenAiResponse; import lombok.*; import java.io.Serializable; import java.util.List; +/** + * 描述: + * + * @author https://www.unfbx.com + * @since 1.1.2 + * 2023-11-12 + */ @Data @JsonIgnoreProperties(ignoreUnknown = true) public class FineTuneJobResponse implements Serializable { @@ -18,7 +24,7 @@ public class FineTuneJobResponse implements Serializable { private Long createdAt; @JsonProperty("error") - private OpenAiResponse.Error error; + private FineTuneError error; @JsonProperty("fine_tuned_model") private String fineTunedModel; @@ -42,7 +48,7 @@ public class FineTuneJobResponse implements Serializable { private List resultFiles; @JsonProperty("status") - private Long status; + private String status; @JsonProperty("trained_tokens") private Integer trainedTokens; @@ -52,4 +58,7 @@ public class FineTuneJobResponse implements Serializable { @JsonProperty("validation_file") private String validationFile; + + @JsonProperty("data") + private String data; } diff --git a/src/main/java/com/unfbx/chatgpt/entity/images/Image.java b/src/main/java/com/unfbx/chatgpt/entity/images/Image.java index 6395e48..6bb2aa3 100644 --- a/src/main/java/com/unfbx/chatgpt/entity/images/Image.java +++ b/src/main/java/com/unfbx/chatgpt/entity/images/Image.java @@ -30,7 +30,8 @@ public class Image implements Serializable { * * @see Model */ - private String model; + @Builder.Default + private String model = Model.DALL_E_3.getName(); /** * 此参数仅仅dall-e-3,默认值:standard diff --git a/src/test/java/com/unfbx/chatgpt/OpenAiClientFunctionTest.java b/src/test/java/com/unfbx/chatgpt/OpenAiClientFunctionTest.java index b01319e..6ab50f5 100644 --- a/src/test/java/com/unfbx/chatgpt/OpenAiClientFunctionTest.java +++ b/src/test/java/com/unfbx/chatgpt/OpenAiClientFunctionTest.java @@ -58,7 +58,7 @@ public void before() { .authInterceptor(new DynamicKeyOpenAiAuthInterceptor()) .okHttpClient(okHttpClient) //自己做了代理就传代理地址,没有可不不传,(关注公众号回复:openai ,获取免费的测试代理地址) - .apiHost("https://dgr.life/") + .apiHost("https://*************/") .build(); openAiStreamClient = OpenAiStreamClient.builder() @@ -69,7 +69,7 @@ public void before() { .authInterceptor(new DynamicKeyOpenAiAuthInterceptor()) .okHttpClient(okHttpClient) //自己做了代理就传代理地址,没有可不不传,(关注公众号回复:openai ,获取免费的测试代理地址) - .apiHost("https://dgr.life/") + .apiHost("https://*************/") .build(); } diff --git a/src/test/java/com/unfbx/chatgpt/OpenAiClientTest.java b/src/test/java/com/unfbx/chatgpt/OpenAiClientTest.java index 13fc43f..5b18ae6 100644 --- a/src/test/java/com/unfbx/chatgpt/OpenAiClientTest.java +++ b/src/test/java/com/unfbx/chatgpt/OpenAiClientTest.java @@ -80,40 +80,16 @@ public void before() { .build(); v2 = OpenAiClient.builder() //支持多key传入,请求时候随机选择 - .apiKey(Arrays.asList("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")) + .apiKey(Arrays.asList("************************")) //自定义key的获取策略:默认KeyRandomStrategy //.keyStrategy(new KeyRandomStrategy()) .keyStrategy(new FirstKeyStrategy()) .okHttpClient(okHttpClient) //自己做了代理就传代理地址,没有可不不传,(关注公众号回复:openai ,获取免费的测试代理地址) - .apiHost("https://dgr.life/") + .apiHost("https://*********/") .build(); } - @Test - public void textToSpeed() throws IOException { - TextToSpeech textToSpeech = TextToSpeech.builder() - .model("tts-1") - .input("In the vast, white expanse of the winter landscape, a drama unfolds that is as timeless as it is raw. Here, in the cradle of nature's harshest trials, a pack of grey wolves has singled out a bison from the herd—a desperate struggle for life and sustenance is about to begin.\n" + - "\n" + - "In a carefully orchestrated assault, the pack encircles their quarry, each wolf keenly aware of its role. Muscles tense and breaths visible in the frigid air, they inch closer, probing for a weakness. The bison, a formidable giant, stands its ground, backed by the survival instincts honed over millennia. Its hulking form casts a solitary shadow against the snow's blinding canvas.\n" + - "\n" + - "The dance of predator and prey plays out as a symphony of survival—each movement, each feint, holds the weight of life itself. The wolves take turns attacking, conserving strength while wearing down their target. The herd, once the bison's allies, scatter into the distance, a stark reminder that in these wild territories, the law of survival supersedes the bonds of kinship.\n" + - "\n" + - "A burst of activity—the wolves close in. The bison, though mighty, is tiring, its breaths labored, its movements sluggish. The wolves sense the turning tide. With relentless determination, they press their advantage, a testament to the brutal beauty of the natural order.\n" + - "\n" + - "As the struggle reaches its inevitable conclusion, we are reminded of the delicate balance that governs these wild spaces. Life, death, struggle, and survival—the cycle continues, each chapter written in the snow, for as long as the wolf roams and the bison roves these frozen plains.") - .voice("alloy") - .responseFormat("mp3") - .build(); - v2.textToSpeech(textToSpeech); - CountDownLatch countDownLatch = new CountDownLatch(1); - try { - countDownLatch.await(); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } @Test public void subscription() { Subscription subscription = v2.subscription(); diff --git a/src/test/java/com/unfbx/chatgpt/OpenAiStreamClientTest.java b/src/test/java/com/unfbx/chatgpt/OpenAiStreamClientTest.java index 2175488..9bcd4b7 100644 --- a/src/test/java/com/unfbx/chatgpt/OpenAiStreamClientTest.java +++ b/src/test/java/com/unfbx/chatgpt/OpenAiStreamClientTest.java @@ -50,13 +50,13 @@ public void before() { .readTimeout(30, TimeUnit.SECONDS) .build(); client = OpenAiStreamClient.builder() - .apiKey(Arrays.asList("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")) + .apiKey(Arrays.asList("*************")) //自定义key的获取策略:默认KeyRandomStrategy // .keyStrategy(new KeyRandomStrategy()) .keyStrategy(new FirstKeyStrategy()) .okHttpClient(okHttpClient) //自己做了代理就传代理地址,没有可不不传((关注公众号回复:openai ,获取免费的测试代理地址)) - .apiHost("https://dgr.life/") + .apiHost("https://**********/") .build(); } diff --git a/src/test/java/com/unfbx/chatgpt/PluginTest.java b/src/test/java/com/unfbx/chatgpt/PluginTest.java index 76a6314..c4d95a7 100644 --- a/src/test/java/com/unfbx/chatgpt/PluginTest.java +++ b/src/test/java/com/unfbx/chatgpt/PluginTest.java @@ -48,12 +48,12 @@ public void before() { openAiClient = OpenAiClient.builder() .okHttpClient(okHttpClient) .apiKey(Arrays.asList("sk-********************************")) - .apiHost("https://dgr.life/") + .apiHost("https://*************/") .build(); openAiStreamClient = OpenAiStreamClient.builder() //支持多key传入,请求时候随机选择 .apiKey(Arrays.asList("sk-********************************")) - .apiHost("https://dgr.life/") + .apiHost("https://*************/") .build(); } diff --git a/src/test/java/com/unfbx/chatgpt/v1_1_2/ConsoleEventSourceListenerV3.java b/src/test/java/com/unfbx/chatgpt/v1_1_2/ConsoleEventSourceListenerV3.java new file mode 100644 index 0000000..e10530b --- /dev/null +++ b/src/test/java/com/unfbx/chatgpt/v1_1_2/ConsoleEventSourceListenerV3.java @@ -0,0 +1,92 @@ +package com.unfbx.chatgpt.v1_1_2; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.json.JSONUtil; +import com.unfbx.chatgpt.entity.chat.ChatCompletionResponse; +import com.unfbx.chatgpt.entity.chat.Message; +import com.unfbx.chatgpt.entity.chat.tool.ToolCallFunction; +import com.unfbx.chatgpt.entity.chat.tool.ToolCalls; +import lombok.Getter; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import okhttp3.Response; +import okhttp3.ResponseBody; +import okhttp3.sse.EventSource; +import okhttp3.sse.EventSourceListener; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.CountDownLatch; + +/** + * 描述: demo测试实现类,仅供思路参考 + * + * @author https:www.unfbx.com + * 2023-11-12 + */ +@Slf4j +public class ConsoleEventSourceListenerV3 extends EventSourceListener { + @Getter + List choices = new ArrayList<>(); + @Getter + ToolCalls toolCalls = new ToolCalls(); + @Getter + ToolCallFunction toolCallFunction = ToolCallFunction.builder().name("").arguments("").build(); + final CountDownLatch countDownLatch; + + public ConsoleEventSourceListenerV3(CountDownLatch countDownLatch) { + this.countDownLatch = countDownLatch; + } + + @Override + public void onOpen(EventSource eventSource, Response response) { + log.info("OpenAI建立sse连接..."); + } + + @Override + public void onEvent(EventSource eventSource, String id, String type, String data) { + log.info("OpenAI返回数据:{}", data); + if (data.equals("[DONE]")) { + log.info("OpenAI返回数据结束了"); + return; + } + ChatCompletionResponse chatCompletionResponse = JSONUtil.toBean(data, ChatCompletionResponse.class); + Message delta = chatCompletionResponse.getChoices().get(0).getDelta(); + if (CollectionUtil.isNotEmpty(delta.getToolCalls())) { + choices.addAll(delta.getToolCalls()); + } + } + + @Override + public void onClosed(EventSource eventSource) { + if(CollectionUtil.isNotEmpty(choices)){ + toolCalls.setId(choices.get(0).getId()); + toolCalls.setType(choices.get(0).getType()); + choices.forEach(e -> { + toolCallFunction.setName(e.getFunction().getName()); + toolCallFunction.setArguments(toolCallFunction.getArguments() + e.getFunction().getArguments()); + toolCalls.setFunction(toolCallFunction); + }); + } + log.info("OpenAI关闭sse连接..."); + countDownLatch.countDown(); + } + + @SneakyThrows + @Override + public void onFailure(EventSource eventSource, Throwable t, Response response) { + if(Objects.isNull(response)){ + log.error("OpenAI sse连接异常:{}", t); + eventSource.cancel(); + return; + } + ResponseBody body = response.body(); + if (Objects.nonNull(body)) { + log.error("OpenAI sse连接异常data:{},异常:{}", body.string(), t); + } else { + log.error("OpenAI sse连接异常data:{},异常:{}", response, t); + } + eventSource.cancel(); + } +} diff --git a/src/test/java/com/unfbx/chatgpt/v1_1_2/OpenAiClientTest.java b/src/test/java/com/unfbx/chatgpt/v1_1_2/OpenAiClientTest.java index cb66676..6b3d4a1 100644 --- a/src/test/java/com/unfbx/chatgpt/v1_1_2/OpenAiClientTest.java +++ b/src/test/java/com/unfbx/chatgpt/v1_1_2/OpenAiClientTest.java @@ -3,26 +3,44 @@ import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; -import com.unfbx.chatgpt.FirstKeyStrategy; -import com.unfbx.chatgpt.OpenAiClient; +import com.unfbx.chatgpt.*; -import com.unfbx.chatgpt.OpenAiClientFunctionTest; +import com.unfbx.chatgpt.entity.Tts.TextToSpeech; +import com.unfbx.chatgpt.entity.Tts.TtsFormat; +import com.unfbx.chatgpt.entity.Tts.TtsVoice; import com.unfbx.chatgpt.entity.chat.*; import com.unfbx.chatgpt.entity.chat.tool.ToolCallFunction; import com.unfbx.chatgpt.entity.chat.tool.ToolCalls; import com.unfbx.chatgpt.entity.chat.tool.Tools; import com.unfbx.chatgpt.entity.chat.tool.ToolsFunction; +import com.unfbx.chatgpt.entity.files.UploadFileResponse; +import com.unfbx.chatgpt.entity.fineTune.job.FineTuneJobEvent; +import com.unfbx.chatgpt.entity.fineTune.job.FineTuneJobListResponse; +import com.unfbx.chatgpt.entity.fineTune.job.FineTuneJobResponse; +import com.unfbx.chatgpt.entity.images.Image; +import com.unfbx.chatgpt.entity.images.ImageResponse; +import com.unfbx.chatgpt.entity.images.SizeEnum; +import com.unfbx.chatgpt.entity.models.Model; +import com.unfbx.chatgpt.function.KeyRandomStrategy; +import com.unfbx.chatgpt.interceptor.DynamicKeyOpenAiAuthInterceptor; import com.unfbx.chatgpt.interceptor.OpenAILogger; import com.unfbx.chatgpt.interceptor.OpenAiResponseInterceptor; import lombok.Builder; import lombok.Data; +import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import okhttp3.OkHttpClient; +import okhttp3.ResponseBody; import okhttp3.logging.HttpLoggingInterceptor; import org.junit.Before; import org.junit.Test; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; +import java.io.*; import java.util.*; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** @@ -35,6 +53,8 @@ public class OpenAiClientTest { private OpenAiClient client; + private OpenAiStreamClient streamClient; + @Before public void before() { @@ -55,13 +75,24 @@ public void before() { .build(); client = OpenAiClient.builder() //支持多key传入,请求时候随机选择 -// .apiKey(Arrays.asList("*********************")) + .apiKey(Arrays.asList("*********************")) //自定义key的获取策略:默认KeyRandomStrategy //.keyStrategy(new KeyRandomStrategy()) .keyStrategy(new FirstKeyStrategy()) .okHttpClient(okHttpClient) //自己做了代理就传代理地址,没有可不不传,(关注公众号回复:openai ,获取免费的测试代理地址) - .apiHost("https://dgr.life/") + .apiHost("https://*************/") + .build(); + + streamClient = OpenAiStreamClient.builder() + //支持多key传入,请求时候随机选择 + .apiKey(Arrays.asList("*********************")) + //自定义key的获取策略:默认KeyRandomStrategy + .keyStrategy(new KeyRandomStrategy()) + .authInterceptor(new DynamicKeyOpenAiAuthInterceptor()) + .okHttpClient(okHttpClient) + //自己做了代理就传代理地址,没有可不不传,(关注公众号回复:openai ,获取免费的测试代理地址) + .apiHost("https://*************/") .build(); } @@ -101,8 +132,6 @@ public void diyReturnModelChat() { @Test public void toolsChat() { - - //模型:GPT_3_5_TURBO_16K_0613 Message message = Message.builder().role(Message.Role.USER).content("给我输出一个长度为2的中文词语,并解释下词语对应物品的用途").build(); //属性一 JSONObject wordLength = new JSONObject(); @@ -161,17 +190,269 @@ public void toolsChat() { .model(ChatCompletion.Model.GPT_4_1106_PREVIEW.getName()) .build(); ChatCompletionResponse chatCompletionResponseV2 = client.chatCompletion(chatCompletionV2); - log.info("自定义的方法返回值:{}",chatCompletionResponseV2.getChoices().get(0).getMessage().getContent()); + log.info("自定义的方法返回值:{}", chatCompletionResponseV2.getChoices().get(0).getMessage().getContent()); } + @Test + public void streamToolsChat() { + + CountDownLatch countDownLatch = new CountDownLatch(1); + ConsoleEventSourceListenerV3 eventSourceListener = new ConsoleEventSourceListenerV3(countDownLatch); + + Message message = Message.builder().role(Message.Role.USER).content("给我输出一个长度为2的中文词语,并解释下词语对应物品的用途").build(); + //属性一 + JSONObject wordLength = new JSONObject(); + wordLength.putOpt("type", "number"); + wordLength.putOpt("description", "词语的长度"); + //属性二 + JSONObject language = new JSONObject(); + language.putOpt("type", "string"); + language.putOpt("enum", Arrays.asList("zh", "en")); + language.putOpt("description", "语言类型,例如:zh代表中文、en代表英语"); + //参数 + JSONObject properties = new JSONObject(); + properties.putOpt("wordLength", wordLength); + properties.putOpt("language", language); + Parameters parameters = Parameters.builder() + .type("object") + .properties(properties) + .required(Collections.singletonList("wordLength")).build(); + Tools tools = Tools.builder() + .type(Tools.Type.FUNCTION.getName()) + .function(ToolsFunction.builder().name("getOneWord").description("获取一个指定长度和语言类型的词语").parameters(parameters).build()) + .build(); + + ChatCompletion chatCompletion = ChatCompletion + .builder() + .messages(Collections.singletonList(message)) + .tools(Collections.singletonList(tools)) + .model(ChatCompletion.Model.GPT_4_1106_PREVIEW.getName()) + .build(); + streamClient.streamChatCompletion(chatCompletion, eventSourceListener); + + try { + countDownLatch.await(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + ToolCalls openAiReturnToolCalls = eventSourceListener.getToolCalls(); + WordParam wordParam = JSONUtil.toBean(openAiReturnToolCalls.getFunction().getArguments(), WordParam.class); + String oneWord = getOneWord(wordParam); + ToolCallFunction tcf = ToolCallFunction.builder().name("getOneWord").arguments(openAiReturnToolCalls.getFunction().getArguments()).build(); + ToolCalls tc = ToolCalls.builder().id(openAiReturnToolCalls.getId()).type(ToolCalls.Type.FUNCTION.getName()).function(tcf).build(); + //构造tool call + Message message2 = Message.builder().role(Message.Role.ASSISTANT).content("方法参数").toolCalls(Collections.singletonList(tc)).build(); + String content + = "{ " + + "\"wordLength\": \"3\", " + + "\"language\": \"zh\", " + + "\"word\": \"" + oneWord + "\"," + + "\"用途\": [\"直接吃\", \"做沙拉\", \"售卖\"]" + + "}"; + Message message3 = Message.builder().toolCallId(openAiReturnToolCalls.getId()).role(Message.Role.TOOL).name("getOneWord").content(content).build(); + List messageList = Arrays.asList(message, message2, message3); + ChatCompletion chatCompletionV2 = ChatCompletion + .builder() + .messages(messageList) + .model(ChatCompletion.Model.GPT_4_1106_PREVIEW.getName()) + .build(); + + + CountDownLatch countDownLatch1 = new CountDownLatch(1); + streamClient.streamChatCompletion(chatCompletionV2, new ConsoleEventSourceListenerV3(countDownLatch)); + try { + countDownLatch1.await(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + try { + countDownLatch1.await(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + } + + + @Test + public void generateImageByDall_e_3() { + Image image = Image.builder() + .responseFormat(com.unfbx.chatgpt.entity.images.ResponseFormat.URL.getName()) + .model(Image.Model.DALL_E_3.getName()) + .prompt("一个咖啡杯,上面印刷Unfbx四个字母。") + .n(1) + .quality(Image.Quality.HD.getName()) + .size(SizeEnum.size_1024_1792.getName()) + .style(Image.Style.NATURAL.getName()) + .build(); + ImageResponse imageResponse = client.genImages(image); +// ImageResponse imageResponse = client.genImages("一个咖啡杯,上面印刷Unfbx四个字母。"); + System.out.println(imageResponse.getData().get(0).getUrl()); + } + + @Test + public void uploadFile() { + UploadFileResponse uploadFileResponse = client.uploadFile(new java.io.File("fine_tune_test_file.json1")); + //file id = file-6KaBdtVlaassk9Y2P5ZjTqIC + //ftjob-eBYBlcF1ZutjEZrT5oSKsmvO + //file-KaNQn5V9YHlLqVQzo8CUMdIr + System.out.println(uploadFileResponse.getId()); + } + + @Test + public void fineTuneJob() { + FineTuneJobResponse fineTuneJobResponse = client.fineTuneJob("file-KaNQn5V9YHlLqVQzo8CUMdIr"); + System.out.println(fineTuneJobResponse.toString()); + //job id = ftjob-5WQr0bZ7grvjnY3Or2sqiixl + } + + @Test + public void fineTuneJobs() { +// FineTuneJobListResponse jobListResponse = client.fineTuneJobs("ftjob-cG7zIraBhAkq5Ybs7311lH7t", 5); + FineTuneJobListResponse jobListResponse = client.fineTuneJobs(null, 20); + System.out.println(jobListResponse); + } + + + @Test + public void retrieveFineTuneJob() { + FineTuneJobResponse fineTuneJobResponse = client.retrieveFineTuneJob("ftjob-5WQr0bZ7grvjnY3Or2sqiixl"); + System.out.println(fineTuneJobResponse); + } + + // + @Test + public void cancelFineTuneJob() { + FineTuneJobResponse fineTuneJobResponse = client.cancelFineTuneJob("ftjob-cG7zIraBhAkq5Ybs7311lH7t"); + System.out.println(fineTuneJobResponse); + } + + @Test + public void fineTuneJobEvents() { + FineTuneJobListResponse listResponse = client.fineTuneJobEvents("ftjob-5WQr0bZ7grvjnY3Or2sqiixl", null, 20); +// FineTuneJobListResponse listResponse = client.fineTuneJobEvents("ftjob-5WQr0bZ7grvjnY3Or2sqiixl", "ftevent-WwB8lpWxhjgUJX9DYdb47zJe", 20); + listResponse.getData().forEach(e -> System.out.println(e.getMessage())); + /** + * The job has successfully completed + * New fine-tuned model created: ft:gpt-3.5-turbo-1106:personal::8K5KwJTU + * Step 91/100: training loss=0.45 + * Step 81/100: training loss=0.00 + * Step 71/100: training loss=0.00 + * Step 61/100: training loss=0.94 + * Step 51/100: training loss=0.19 + * Step 41/100: training loss=0.06 + * Step 31/100: training loss=0.95 + * Step 21/100: training loss=1.99 + * Step 11/100: training loss=2.50 + * Step 1/100: training loss=5.42 + * Fine-tuning job started + * Files validated, moving job to queued state + * Validating training file: file-KaNQn5V9YHlLqVQzo8CUMdIr + * Created fine-tuning job: ftjob-5WQr0bZ7grvjnY3Or2sqiixl + * + * Process finished with exit code 0 + */ + } + + @Test + public void fineTuneJobModelChat() { + Message message1 = Message.builder().role(Message.Role.SYSTEM).content("OnBot是一个聊天机器人。").build(); + Message message2 = Message.builder().role(Message.Role.USER).content("OnBot请问:Chatgpt-java的作者是谁?").build(); + List messages = new ArrayList<>(2); + messages.add(message1); + messages.add(message2); + ChatCompletion chatCompletion = ChatCompletion + .builder() + .messages(messages) + .model("ft:gpt-3.5-turbo-1106:personal::8K5KwJTU") + .build(); + ChatCompletionResponse chatCompletionResponse = client.chatCompletion(chatCompletion); + chatCompletionResponse.getChoices().forEach(e -> { + System.out.println(e.getMessage()); + //返回值:Message(content=作者是Unfbx,个人网站:https://www.unfbx.com) + }); + } + + @Test + public void models() { + List models = client.models(); + System.out.println(models); + } + + + @Test + public void textToSpeed() { + TextToSpeech textToSpeech = TextToSpeech.builder() + .model(TextToSpeech.Model.TTS_1_HD.getName()) + .input("OpenAI官方Api的Java SDK,可以快速接入项目使用。目前支持OpenAI官方全部接口,同时支持Tokens计算。官方github地址:https://github.com/Grt1228/chatgpt-java。欢迎star。") + .voice(TtsVoice.NOVA.getName()) + .responseFormat(TtsFormat.MP3.getName()) + .build(); + File file = new File("C:\\Users\\***\\Desktop\\test.mp3"); + client.textToSpeech(textToSpeech, new Callback() { + @SneakyThrows + @Override + public void onResponse(Call call, Response response) { + InputStream inputStream = response.body().byteStream(); + //创建文件 + if (!file.exists()) { + if (!file.getParentFile().exists()) + file.getParentFile().mkdir(); + try { + file.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + log.error("createNewFile IOException"); + } + } + + OutputStream os = null; + try { + os = new BufferedOutputStream(new FileOutputStream(file)); + byte data[] = new byte[8192]; + int len; + while ((len = inputStream.read(data, 0, 8192)) != -1) { + os.write(data, 0, len); + } + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + inputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + try { + if (os != null) { + os.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + @Override + public void onFailure(Call call, Throwable t) { + + } + }); + CountDownLatch countDownLatch = new CountDownLatch(1); + try { + countDownLatch.await(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } /** * 获取一个词语 + * * @param wordParam * @return */