├── .gitignore
├── README.md
├── pom.xml
└── src
├── main
├── java
│ └── com
│ │ └── asleepyfish
│ │ ├── ChatGPTDemoApplication.java
│ │ ├── config
│ │ ├── ClientConfiguration.java
│ │ └── CorsConfiguration.java
│ │ ├── controller
│ │ ├── ChatGPTController.java
│ │ └── MainTest.java
│ │ └── strategy
│ │ └── SelectSecondStrategy.java
└── resources
│ ├── application.yml
│ ├── audio
│ └── 想象之中-许嵩.mp3
│ └── image
│ ├── img.png
│ └── mask.png
└── test
└── java
└── com
└── asleepyfish
├── ChatgptDemoApplicationTests.java
└── test
└── ChatGPTTest.java
/.gitignore:
--------------------------------------------------------------------------------
1 | HELP.md
2 | target/
3 | !.mvn/wrapper/maven-wrapper.jar
4 | !**/src/main/**/target/
5 | !**/src/test/**/target/
6 |
7 | ### STS ###
8 | .apt_generated
9 | .classpath
10 | .factorypath
11 | .project
12 | .settings
13 | .springBeans
14 | .sts4-cache
15 |
16 | ### IntelliJ IDEA ###
17 | .idea
18 | *.iws
19 | *.iml
20 | *.ipr
21 |
22 | ### NetBeans ###
23 | /nbproject/private/
24 | /nbbuild/
25 | /dist/
26 | /nbdist/
27 | /.nb-gradle/
28 | build/
29 | !**/src/main/**/build/
30 | !**/src/test/**/build/
31 |
32 | ### VS Code ###
33 | .vscode/
34 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | `pom.xml`中引入依赖
2 | - Latest Version: 
3 | - Maven:
4 | ```xml
5 |
6 | io.github.asleepyfish
7 | chatgpt
8 | Latest Version
9 |
10 | ```
11 | **注意:所有代码示例均有基于和SpringBoot和直接Main方法调用两种实现。分别在类`MainTest`和类`ChatGPTController`中。**
12 |
13 | `master`分支及`dev-trunk`分支代码为`SpringBoot2`最新开发代码Demo
14 |
15 | `dev-springboot3`分支为`SpringBoot3`最新开发代码Demo
16 |
17 | 两者在开发时除了部分引用包的版本或路径不同外,其他代码完全一致。
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | org.springframework.boot
7 | spring-boot-starter-parent
8 | 2.7.11
9 |
10 |
11 | com.asleepyfish
12 | chatgpt-demo
13 | 0.0.1-SNAPSHOT
14 | chatgpt-demo
15 | chatgpt-demo
16 |
17 | 1.8
18 | 1.3.6
19 |
20 |
21 |
22 | cn.hutool
23 | hutool-http
24 | 5.7.3
25 |
26 |
27 |
28 | org.springframework.boot
29 | spring-boot-starter-web
30 |
31 |
32 |
33 | org.springframework.boot
34 | spring-boot-starter-test
35 |
36 |
37 |
38 | io.github.asleepyfish
39 | chatgpt
40 | ${chatgpt.version}
41 |
42 |
43 |
44 | org.projectlombok
45 | lombok
46 |
47 |
48 |
49 |
50 |
51 |
52 | org.springframework.boot
53 | spring-boot-maven-plugin
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/src/main/java/com/asleepyfish/ChatGPTDemoApplication.java:
--------------------------------------------------------------------------------
1 | package com.asleepyfish;
2 |
3 | import io.github.asleepyfish.annotation.EnableChatGPT;
4 | import org.springframework.boot.SpringApplication;
5 | import org.springframework.boot.autoconfigure.SpringBootApplication;
6 |
7 | @SpringBootApplication
8 | @EnableChatGPT
9 | public class ChatGPTDemoApplication {
10 |
11 | public static void main(String[] args) {
12 | SpringApplication.run(ChatGPTDemoApplication.class, args);
13 | }
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/java/com/asleepyfish/config/ClientConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.asleepyfish.config;
2 |
3 | import io.github.asleepyfish.config.ChatGPTProperties;
4 | import org.springframework.beans.factory.annotation.Autowired;
5 | import org.springframework.context.annotation.Configuration;
6 |
7 | /**
8 | * @Author: asleepyfish
9 | * @Date: 2023-08-06 21:52
10 | * @Description: client configuration
11 | */
12 | @Configuration
13 | public class ClientConfiguration {
14 |
15 | @Autowired
16 | private ChatGPTProperties properties;
17 |
18 | /* @Bean
19 | public OkHttpClient okHttpClient() {
20 | Dispatcher dispatcher = new Dispatcher();
21 | dispatcher.setMaxRequests(100);
22 | dispatcher.setMaxRequestsPerHost(10);
23 | return new OkHttpClient.Builder()
24 | .addInterceptor(new AuthenticationInterceptor(properties))
25 | .connectionPool(new ConnectionPool(100, 10, TimeUnit.SECONDS))
26 | .readTimeout(Duration.ZERO.toMillis(), TimeUnit.MILLISECONDS)
27 | .connectTimeout(Duration.ZERO.toMillis(), TimeUnit.MILLISECONDS)
28 | .hostnameVerifier((hostname, session) -> true)
29 | .proxy(new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(properties.getProxyHost(), properties.getProxyPort())))
30 | .proxyAuthenticator((route, response) -> {
31 | String credential = Credentials.basic("proxyUsername", "proxyPassword");
32 | return response.request().newBuilder()
33 | .header("Proxy-Authorization", credential)
34 | .build();
35 | })
36 | .dispatcher(dispatcher)
37 | .build();
38 | }*/
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/java/com/asleepyfish/config/CorsConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.asleepyfish.config;
2 |
3 | import org.springframework.context.annotation.Configuration;
4 | import org.springframework.web.servlet.config.annotation.CorsRegistry;
5 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
6 |
7 | /**
8 | * @Author: asleepyfish
9 | * @Date: 2023-04-24 00:08
10 | * @Description: 跨域配置
11 | */
12 | @Configuration
13 | public class CorsConfiguration implements WebMvcConfigurer {
14 | @Override
15 | public void addCorsMappings(CorsRegistry registry) {
16 | registry.addMapping("/**")
17 | //是否发送Cookie
18 | .allowCredentials(true)
19 | //放行哪些原始域
20 | .allowedOriginPatterns("*")
21 | .allowedMethods("GET", "POST", "PUT", "DELETE")
22 | .allowedHeaders("*")
23 | .exposedHeaders("*");
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/com/asleepyfish/controller/ChatGPTController.java:
--------------------------------------------------------------------------------
1 | package com.asleepyfish.controller;
2 |
3 | import com.knuddels.jtokkit.api.ModelType;
4 | import com.theokanning.openai.completion.CompletionRequest;
5 | import com.theokanning.openai.embedding.EmbeddingRequest;
6 | import com.theokanning.openai.finetune.FineTuneRequest;
7 | import com.theokanning.openai.image.CreateImageEditRequest;
8 | import com.theokanning.openai.image.CreateImageRequest;
9 | import com.theokanning.openai.image.CreateImageVariationRequest;
10 | import com.theokanning.openai.image.ImageResult;
11 | import com.theokanning.openai.moderation.ModerationRequest;
12 | import io.github.asleepyfish.config.ChatGPTProperties;
13 | import io.github.asleepyfish.entity.billing.Billing;
14 | import io.github.asleepyfish.entity.billing.Subscription;
15 | import io.github.asleepyfish.enums.audio.AudioResponseFormatEnum;
16 | import io.github.asleepyfish.enums.edit.EditModelEnum;
17 | import io.github.asleepyfish.enums.embedding.EmbeddingModelEnum;
18 | import io.github.asleepyfish.enums.image.ImageResponseFormatEnum;
19 | import io.github.asleepyfish.enums.image.ImageSizeEnum;
20 | import io.github.asleepyfish.service.OpenAiProxyService;
21 | import io.github.asleepyfish.util.OpenAiUtils;
22 | import org.springframework.web.bind.annotation.GetMapping;
23 | import org.springframework.web.bind.annotation.PostMapping;
24 | import org.springframework.web.bind.annotation.RestController;
25 |
26 | import javax.servlet.http.HttpServletResponse;
27 | import java.io.IOException;
28 | import java.util.Arrays;
29 | import java.util.List;
30 |
31 | /**
32 | * @Author: asleepyfish
33 | * @Date: 2023-02-18 14:44
34 | * @Description: 注意:所有代码示例均有基于和SpringBoot和直接Main方法调用两种实现。分别在类MainTest和类ChatGPTController中。
35 | */
36 | @RestController
37 | public class ChatGPTController {
38 |
39 | /**
40 | * 问答
41 | *
42 | * @param content 问题
43 | * @return 答案
44 | */
45 | @GetMapping("/chat")
46 | public List chat(String content) {
47 | return OpenAiUtils.createChatCompletion(content);
48 | }
49 |
50 | /**
51 | * 流式问答,返回到控制台
52 | */
53 | @GetMapping("/streamChat")
54 | public void streamChat(String content) {
55 | // OpenAiUtils.createStreamChatCompletion(content, System.out);
56 | // 下面的默认和上面这句代码一样,是输出结果到控制台
57 | OpenAiUtils.createStreamChatCompletion(content);
58 | }
59 |
60 | /**
61 | * 流式问答,输出结果到WEB浏览器端
62 | */
63 | @GetMapping("/streamChatWithWeb")
64 | public void streamChatWithWeb(String content, HttpServletResponse response) throws IOException {
65 | // 需要指定response的ContentType为流式输出,且字符编码为UTF-8
66 | response.setContentType("text/event-stream");
67 | response.setCharacterEncoding("UTF-8");
68 | // 禁用缓存
69 | response.setHeader("Cache-Control", "no-cache");
70 | OpenAiUtils.createStreamChatCompletion(content, response.getOutputStream());
71 | }
72 |
73 | /**
74 | * 生成图片
75 | *
76 | * @param prompt 图片描述
77 | */
78 | @PostMapping("/createImage")
79 | public List createImage(String prompt) {
80 | List imageList = OpenAiUtils.createImage(prompt);
81 | System.out.println(imageList);
82 | return imageList;
83 | }
84 |
85 | /**
86 | * 下载图片
87 | */
88 | @GetMapping("/downloadImage")
89 | public void downloadImage(String prompt, Integer imageNum, HttpServletResponse response) throws IOException {
90 | if (imageNum == null || imageNum < 1) {
91 | imageNum = 1;
92 | }
93 | if (imageNum == 1) {
94 | response.setContentType("image/png");
95 | response.setHeader("Content-Disposition", "attachment; filename=generated.png");
96 | OpenAiUtils.downloadImage(prompt, response.getOutputStream());
97 | } else {
98 | // 图片数量大于1时,下载的是zip压缩包
99 | response.setContentType("application/zip");
100 | response.setHeader("Content-Disposition", "attachment; filename=images.zip");
101 | CreateImageRequest createImageRequest = CreateImageRequest.builder()
102 | .prompt(prompt)
103 | .n(imageNum)
104 | .build();
105 | OpenAiUtils.downloadImage(createImageRequest, response.getOutputStream());
106 | }
107 | }
108 |
109 | @PostMapping("/billing")
110 | public void billing() {
111 | String monthUsage = OpenAiUtils.billingUsage("2023-04-01", "2023-05-01");
112 | System.out.println("四月使用:" + monthUsage + "美元");
113 | String totalUsage = OpenAiUtils.billingUsage();
114 | System.out.println("一共使用:" + totalUsage + "美元");
115 | String stageUsage = OpenAiUtils.billingUsage("2023-01-31");
116 | System.out.println("自从2023/01/31使用:" + stageUsage + "美元");
117 | Subscription subscription = OpenAiUtils.subscription();
118 | System.out.println("订阅信息(包含到期日期,账户总额度等信息):" + subscription);
119 | // dueDate为到期日,total为总额度,usage为使用量,balance为余额
120 | Billing totalBilling = OpenAiUtils.billing();
121 | System.out.println("历史账单信息:" + totalBilling);
122 | // 默认不传参的billing方法的使用量usage从2023-01-01开始,如果用户的账单使用早于该日期,可以传入开始日期startDate
123 | Billing posibleStartBilling = OpenAiUtils.billing("2022-01-01");
124 | System.out.println("可能的历史账单信息:" + posibleStartBilling);
125 | }
126 |
127 | /**
128 | * 自定义Token使用(解决单个SpringBoot项目中只能指定唯一的Token[sk-xxxxxxxxxxxxx]的问题,现在可以自定义ChatGPTProperties内容,添加更多的Token实例)
129 | */
130 | @PostMapping("/customToken")
131 | public void customToken() {
132 | ChatGPTProperties properties = ChatGPTProperties.builder().token("sk-002xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
133 | .proxyHost("127.0.0.1")
134 | .proxyHost("7890")
135 | .build();
136 | OpenAiProxyService openAiProxyService = new OpenAiProxyService(properties);
137 | // 直接使用new出来的openAiProxyService来调用方法,每个OpenAiProxyService都拥有自己的Token。
138 | // 这样在一个SpringBoot项目中,就可以有多个Token,可以有更多的免费额度供使用了
139 | openAiProxyService.createStreamChatCompletion("Java的三大特性是什么");
140 | }
141 |
142 | @PostMapping("/models")
143 | public void models() {
144 | System.out.println("models列表:" + OpenAiUtils.listModels());
145 | System.out.println("=============================================");
146 | System.out.println("text-davinci-003信息:" + OpenAiUtils.getModel("text-davinci-003"));
147 | }
148 |
149 | /**
150 | * 编辑
151 | */
152 | @PostMapping("/edit")
153 | public void edit() {
154 | String input = "What day of the wek is it?";
155 | String instruction = "Fix the spelling mistakes";
156 | System.out.println("编辑前:" + input);
157 | // 下面这句和OpenAiUtils.edit(input, instruction, EditModelEnum.TEXT_DAVINCI_EDIT_001);是一样的,默认使用模型TEXT_DAVINCI_EDIT_001
158 | System.out.println("编辑后:" + OpenAiUtils.edit(input, instruction));
159 | System.out.println("=============================================");
160 | input = " public static void mian(String[] args) {\n" +
161 | " system.in.println(\"hello world\");\n" +
162 | " }";
163 | instruction = "Fix the code mistakes";
164 | System.out.println("修正代码前:\n" + input);
165 | System.out.println("修正代码后:\n" + OpenAiUtils.edit(input, instruction, EditModelEnum.CODE_DAVINCI_EDIT_001));
166 | }
167 |
168 | @PostMapping("/embeddings")
169 | public void embeddings() {
170 | String text = "Once upon a time";
171 | System.out.println("文本:" + text);
172 | System.out.println("文本的嵌入向量:" + OpenAiUtils.embeddings(text));
173 | System.out.println("=============================================");
174 | String[] texts = {"Once upon a time", "There was a princess"};
175 | System.out.println("文本数组:" + Arrays.toString(texts));
176 | EmbeddingRequest embeddingRequest = EmbeddingRequest.builder()
177 | .model(EmbeddingModelEnum.TEXT_EMBEDDING_ADA_002.getModelName()).input(Arrays.asList(texts)).build();
178 | System.out.println("文本数组的嵌入向量:" + OpenAiUtils.embeddings(embeddingRequest));
179 | }
180 |
181 | @PostMapping("/transcription")
182 | public void transcription() {
183 | String filePath = "src/main/resources/audio/想象之中-许嵩.mp3";
184 | System.out.println("语音文件转录后的text文本是:" + OpenAiUtils.transcription(filePath, AudioResponseFormatEnum.TEXT));
185 | // File file = new File("src/main/resources/audio/想象之中-许嵩.mp3");
186 | // System.out.println("语音文件转录后的text文本是:" + OpenAiUtils.transcription(file, AudioResponseFormatEnum.TEXT));
187 | }
188 |
189 | @PostMapping("/translation")
190 | public void translation() {
191 | String filePath = "src/main/resources/audio/想象之中-许嵩.mp3";
192 | System.out.println("语音文件翻译成英文后的text文本是:" + OpenAiUtils.translation(filePath, AudioResponseFormatEnum.TEXT));
193 | // File file = new File("src/main/resources/audio/想象之中-许嵩.mp3");
194 | // System.out.println("语音文件翻译成英文后的text文本是:" + OpenAiUtils.translation(file, AudioResponseFormatEnum.TEXT));
195 | }
196 |
197 | @PostMapping("/createImageEdit")
198 | public void createImageEdit() {
199 | CreateImageEditRequest createImageEditRequest = CreateImageEditRequest.builder().prompt("Background changed to white")
200 | .n(1).size(ImageSizeEnum.S512x512.getSize()).responseFormat(ImageResponseFormatEnum.URL.getResponseFormat()).build();
201 | ImageResult imageEdit = OpenAiUtils.createImageEdit(createImageEditRequest, "src/main/resources/image/img.png", "src/main/resources/image/mask.png");
202 | System.out.println("图片编辑结果:" + imageEdit);
203 | }
204 |
205 | @PostMapping("/createImageVariation")
206 | public void createImageVariation() {
207 | CreateImageVariationRequest createImageVariationRequest = CreateImageVariationRequest.builder()
208 | .n(2).size(ImageSizeEnum.S512x512.getSize()).responseFormat(ImageResponseFormatEnum.URL.getResponseFormat()).build();
209 | ImageResult imageVariation = OpenAiUtils.createImageVariation(createImageVariationRequest, "src/main/resources/image/img.png");
210 | System.out.println("图片变体结果:" + imageVariation);
211 | }
212 |
213 | /**
214 | * 文件操作(下面文件操作入参,用户可根据实际情况自行补全)
215 | */
216 | @PostMapping("/files")
217 | public void files() {
218 | // 上传文件
219 | System.out.println("上传文件信息:" + OpenAiUtils.uploadFile("", ""));
220 | // 获取文件列表
221 | System.out.println("文件列表:" + OpenAiUtils.listFiles());
222 | // 获取文件信息
223 | System.out.println("文件信息:" + OpenAiUtils.retrieveFile(""));
224 | // 获取文件内容
225 | System.out.println("文件内容:" + OpenAiUtils.retrieveFileContent(""));
226 | // 删除文件
227 | System.out.println("删除文件信息:" + OpenAiUtils.deleteFile(""));
228 | }
229 |
230 | @PostMapping("/fileTune")
231 | public void fileTune() {
232 | // 创建微调
233 | FineTuneRequest fineTuneRequest = FineTuneRequest.builder().trainingFile("").build();
234 | System.out.println("创建微调信息:" + OpenAiUtils.createFineTune(fineTuneRequest));
235 | // 创建微调完成
236 | CompletionRequest completionRequest = CompletionRequest.builder().build();
237 | System.out.println("创建微调完成信息:" + OpenAiUtils.createFineTuneCompletion(completionRequest));
238 | // 获取微调列表
239 | System.out.println("获取微调列表:" + OpenAiUtils.listFineTunes());
240 | // 获取微调信息
241 | System.out.println("获取微调信息:" + OpenAiUtils.retrieveFineTune(""));
242 | // 取消微调
243 | System.out.println("取消微调信息:" + OpenAiUtils.cancelFineTune(""));
244 | // 列出微调事件
245 | System.out.println("列出微调事件:" + OpenAiUtils.listFineTuneEvents(""));
246 | // 删除微调
247 | System.out.println("删除微调信息:" + OpenAiUtils.deleteFineTune(""));
248 | }
249 |
250 | @PostMapping("/moderation")
251 | public void moderation() {
252 | // 创建moderation
253 | ModerationRequest moderationRequest = ModerationRequest.builder().input("I want to kill them.").build();
254 | System.out.println("创建moderation信息:" + OpenAiUtils.createModeration(moderationRequest));
255 | }
256 |
257 | @PostMapping("/baseUrl")
258 | public void baseUrl() {
259 | // 先在application.yml中配置chatgpt.base-url
260 | System.out.println("models列表:" + OpenAiUtils.listModels());
261 | }
262 |
263 | @GetMapping("/systemPrompt")
264 | public void systemPrompt(String systemPrompt, String content, HttpServletResponse response) throws IOException {
265 | System.out.println("初始系统级提示信息为:" + OpenAiUtils.getSystemPrompt());
266 | // OpenAiUtils.setSystemPrompt("我是一个Java开发工程师,所有的代码请求都请用Java给我生成。");
267 | OpenAiUtils.setSystemPrompt(systemPrompt);
268 | // OpenAiUtils.createStreamChatCompletion("写一个迭代器模式的代码");
269 | // 需要指定response的ContentType为流式输出,且字符编码为UTF-8
270 | response.setContentType("text/event-stream");
271 | response.setCharacterEncoding("UTF-8");
272 | // 禁用缓存
273 | response.setHeader("Cache-Control", "no-cache");
274 | OpenAiUtils.createStreamChatCompletion(content, response.getOutputStream());
275 | // System.out.println("当前的系统级信息提示为:" + OpenAiUtils.getSystemPrompt());
276 | // 清理系统级提示信息
277 | // OpenAiUtils.cleanUpSystemPrompt();
278 | // System.out.println("清理后的系统级提示信息为:" + OpenAiUtils.getSystemPrompt());
279 | }
280 |
281 | @GetMapping("/countTokens")
282 | public void countTokens(String text) {
283 | System.out.println("当前输入文字使用模型[gpt-3.5-turbo] token总数为:" + OpenAiUtils.countTokens(text));
284 | ModelType modelType = ModelType.GPT_4_32K;
285 | // 实际上单就计算的token的数目上来说3.5和4是一样的
286 | System.out.println("当前输入文字使用模型[gpt-4-32k] token总数为:" + OpenAiUtils.countTokens(text, modelType));
287 | }
288 | }
--------------------------------------------------------------------------------
/src/main/java/com/asleepyfish/controller/MainTest.java:
--------------------------------------------------------------------------------
1 | package com.asleepyfish.controller;
2 |
3 | import com.asleepyfish.strategy.SelectSecondStrategy;
4 | import com.knuddels.jtokkit.api.ModelType;
5 | import com.theokanning.openai.completion.CompletionRequest;
6 | import com.theokanning.openai.embedding.EmbeddingRequest;
7 | import com.theokanning.openai.finetune.FineTuneRequest;
8 | import com.theokanning.openai.image.CreateImageEditRequest;
9 | import com.theokanning.openai.image.CreateImageVariationRequest;
10 | import com.theokanning.openai.image.ImageResult;
11 | import com.theokanning.openai.moderation.ModerationRequest;
12 | import io.github.asleepyfish.config.ChatGPTProperties;
13 | import io.github.asleepyfish.entity.billing.Billing;
14 | import io.github.asleepyfish.entity.billing.Subscription;
15 | import io.github.asleepyfish.enums.audio.AudioResponseFormatEnum;
16 | import io.github.asleepyfish.enums.edit.EditModelEnum;
17 | import io.github.asleepyfish.enums.embedding.EmbeddingModelEnum;
18 | import io.github.asleepyfish.enums.image.ImageResponseFormatEnum;
19 | import io.github.asleepyfish.enums.image.ImageSizeEnum;
20 | import io.github.asleepyfish.service.OpenAiProxyService;
21 | import org.junit.jupiter.api.Test;
22 |
23 | import java.util.Arrays;
24 |
25 | /**
26 | * @Author: asleepyfish
27 | * @Date: 2023-06-11 21:18
28 | * @Description: 注意:所有代码示例均有基于和SpringBoot和直接Main方法调用两种实现。分别在类MainTest和类ChatGPTController中。
29 | */
30 | public class MainTest {
31 |
32 | @Test
33 | public void chat() {
34 | ChatGPTProperties properties = ChatGPTProperties.builder().token("sk-xxx")
35 | .proxyHost("127.0.0.1")
36 | .proxyPort(7890)
37 | .build();
38 | OpenAiProxyService openAiProxyService = new OpenAiProxyService(properties);
39 | System.out.println(openAiProxyService.chatCompletion("Go写个程序"));
40 | }
41 |
42 | @Test
43 | public void streamChat() {
44 | ChatGPTProperties properties = ChatGPTProperties.builder().token("sk-xxx")
45 | .proxyHost("127.0.0.1")
46 | .proxyPort(7890)
47 | .build();
48 | OpenAiProxyService openAiProxyService = new OpenAiProxyService(properties);
49 | openAiProxyService.createStreamChatCompletion("杭州旅游攻略");
50 | }
51 |
52 | @Test
53 | public void createImages() {
54 | ChatGPTProperties properties = ChatGPTProperties.builder().token("sk-xxx")
55 | .proxyHost("127.0.0.1")
56 | .proxyPort(7890)
57 | .build();
58 | OpenAiProxyService openAiProxyService = new OpenAiProxyService(properties);
59 | System.out.println(openAiProxyService.createImages("大白狗"));
60 | }
61 |
62 | @Test
63 | public void billing() {
64 | ChatGPTProperties properties = ChatGPTProperties.builder().token("sk-xxx")
65 | .proxyHost("127.0.0.1")
66 | .proxyPort(7890)
67 | .build();
68 | OpenAiProxyService openAiProxyService = new OpenAiProxyService(properties);
69 | String monthUsage = openAiProxyService.billingUsage("2023-04-01", "2023-05-01");
70 | System.out.println("四月使用:" + monthUsage + "美元");
71 | String totalUsage = openAiProxyService.billingUsage();
72 | System.out.println("一共使用:" + totalUsage + "美元");
73 | String stageUsage = openAiProxyService.billingUsage("2023-01-31");
74 | System.out.println("自从2023/01/31使用:" + stageUsage + "美元");
75 | Subscription subscription = openAiProxyService.subscription();
76 | System.out.println("订阅信息(包含到期日期,账户总额度等信息):" + subscription);
77 | // dueDate为到期日,total为总额度,usage为使用量,balance为余额
78 | Billing totalBilling = openAiProxyService.billing();
79 | System.out.println("历史账单信息:" + totalBilling);
80 | // 默认不传参的billing方法的使用量usage从2023-01-01开始,如果用户的账单使用早于该日期,可以传入开始日期startDate
81 | Billing posibleStartBilling = openAiProxyService.billing("2022-01-01");
82 | System.out.println("可能的历史账单信息:" + posibleStartBilling);
83 | }
84 |
85 | @Test
86 | public void model() {
87 | ChatGPTProperties properties = ChatGPTProperties.builder().token("sk-xxx")
88 | .proxyHost("127.0.0.1")
89 | .proxyPort(7890)
90 | .build();
91 | OpenAiProxyService openAiProxyService = new OpenAiProxyService(properties);
92 | System.out.println("models列表:" + openAiProxyService.listModels());
93 | System.out.println("=============================================");
94 | System.out.println("text-davinci-003信息:" + openAiProxyService.getModel("text-davinci-003"));
95 | }
96 |
97 | @Test
98 | public void edit() {
99 | ChatGPTProperties properties = ChatGPTProperties.builder().token("sk-xxx")
100 | .proxyHost("127.0.0.1")
101 | .proxyPort(7890)
102 | .build();
103 | OpenAiProxyService openAiProxyService = new OpenAiProxyService(properties);
104 | String input = "What day of the wek is it?";
105 | String instruction = "Fix the spelling mistakes";
106 | System.out.println("编辑前:" + input);
107 | // 下面这句和openAiProxyService.edit(input, instruction, EditModelEnum.TEXT_DAVINCI_EDIT_001);是一样的,默认使用模型TEXT_DAVINCI_EDIT_001
108 | System.out.println("编辑后:" + openAiProxyService.edit(input, instruction));
109 | System.out.println("=============================================");
110 | input = " public static void mian([String] args) {\n" +
111 | " system.in.println(\"hello world\");\n" +
112 | " }";
113 | instruction = "Fix the code mistakes";
114 | System.out.println("修正代码前:\n" + input);
115 | System.out.println("修正代码后:\n" + openAiProxyService.edit(input, instruction, EditModelEnum.CODE_DAVINCI_EDIT_001));
116 | }
117 |
118 | @Test
119 | public void embeddings() {
120 | ChatGPTProperties properties = ChatGPTProperties.builder().token("sk-xxx")
121 | .proxyHost("127.0.0.1")
122 | .proxyPort(7890)
123 | .build();
124 | OpenAiProxyService openAiProxyService = new OpenAiProxyService(properties);
125 | // 单文本
126 | String text = "Once upon a time";
127 | System.out.println("文本:" + text);
128 | System.out.println("文本的嵌入向量:" + openAiProxyService.embeddings(text));
129 | System.out.println("=============================================");
130 | // 文本数组
131 | String[] texts = {"Once upon a time", "There was a princess"};
132 | System.out.println("文本数组:" + Arrays.toString(texts));
133 | EmbeddingRequest embeddingRequest = EmbeddingRequest.builder()
134 | .model(EmbeddingModelEnum.TEXT_EMBEDDING_ADA_002.getModelName()).input(Arrays.asList(texts)).build();
135 | System.out.println("文本数组的嵌入向量:" + openAiProxyService.embeddings(embeddingRequest));
136 | }
137 |
138 | @Test
139 | public void transcription() {
140 | ChatGPTProperties properties = ChatGPTProperties.builder().token("sk-xxx")
141 | .proxyHost("127.0.0.1")
142 | .proxyPort(7890)
143 | .build();
144 | OpenAiProxyService openAiProxyService = new OpenAiProxyService(properties);
145 | String filePath = "src/main/resources/audio/想象之中-许嵩.mp3";
146 | System.out.println("语音文件转录后的json文本是:" + openAiProxyService.transcription(filePath, AudioResponseFormatEnum.JSON));
147 | // File file = new File("src/main/resources/audio/想象之中-许嵩.mp3");
148 | // System.out.println("语音文件转录后的json文本是:" + openAiProxyService.transcription(file, AudioResponseFormatEnum.JSON));
149 | }
150 |
151 | @Test
152 | public void translation() {
153 | ChatGPTProperties properties = ChatGPTProperties.builder().token("sk-xxx")
154 | .proxyHost("127.0.0.1")
155 | .proxyPort(7890)
156 | .build();
157 | OpenAiProxyService openAiProxyService = new OpenAiProxyService(properties);
158 | String filePath = "src/main/resources/audio/想象之中-许嵩.mp3";
159 | System.out.println("语音文件翻译成英文后的json文本是:" + openAiProxyService.translation(filePath, AudioResponseFormatEnum.JSON));
160 | // File file = new File("src/main/resources/audio/想象之中-许嵩.mp3");
161 | // System.out.println("语音文件翻译成英文后的json文本是:" + openAiProxyService.translation(file, AudioResponseFormatEnum.JSON));
162 | }
163 |
164 | @Test
165 | public void createImageEdit() {
166 | ChatGPTProperties properties = ChatGPTProperties.builder().token("sk-xxx")
167 | .proxyHost("127.0.0.1")
168 | .proxyPort(7890)
169 | .build();
170 | OpenAiProxyService openAiProxyService = new OpenAiProxyService(properties);
171 | CreateImageEditRequest createImageEditRequest = CreateImageEditRequest.builder().prompt("A sunlit indoor lounge area with a pool containing a flamingo")
172 | .n(1).size(ImageSizeEnum.S512x512.getSize()).responseFormat(ImageResponseFormatEnum.URL.getResponseFormat()).build();
173 | ImageResult imageEdit = openAiProxyService.createImageEdit(createImageEditRequest, "src/main/resources/image/img.png", "src/main/resources/image/mask.png");
174 | System.out.println("图片编辑结果:" + imageEdit);
175 | }
176 |
177 | @Test
178 | public void createImageVariation() {
179 | ChatGPTProperties properties = ChatGPTProperties.builder().token("sk-xxx")
180 | .proxyHost("127.0.0.1")
181 | .proxyPort(7890)
182 | .build();
183 | OpenAiProxyService openAiProxyService = new OpenAiProxyService(properties);
184 | CreateImageVariationRequest createImageVariationRequest = CreateImageVariationRequest.builder()
185 | .n(2).size(ImageSizeEnum.S512x512.getSize()).responseFormat(ImageResponseFormatEnum.URL.getResponseFormat()).build();
186 | ImageResult imageVariation = openAiProxyService.createImageVariation(createImageVariationRequest, "src/main/resources/image/img.png");
187 | System.out.println("图片变体结果:" + imageVariation);
188 | }
189 |
190 | /**
191 | * 文件操作(下面文件操作入参,用户可根据实际情况自行补全)
192 | */
193 | @Test
194 | public void files() {
195 | ChatGPTProperties properties = ChatGPTProperties.builder().token("sk-xxx")
196 | .proxyHost("127.0.0.1")
197 | .proxyPort(7890)
198 | .build();
199 | OpenAiProxyService openAiProxyService = new OpenAiProxyService(properties);
200 | // 上传文件
201 | System.out.println("上传文件信息:" + openAiProxyService.uploadFile("", ""));
202 | // 获取文件列表
203 | System.out.println("文件列表:" + openAiProxyService.listFiles());
204 | // 获取文件信息
205 | System.out.println("文件信息:" + openAiProxyService.retrieveFile(""));
206 | // 获取文件内容
207 | System.out.println("文件内容:" + openAiProxyService.retrieveFileContent(""));
208 | // 删除文件
209 | System.out.println("删除文件信息:" + openAiProxyService.deleteFile(""));
210 | }
211 |
212 | @Test
213 | public void fileTune() {
214 | ChatGPTProperties properties = ChatGPTProperties.builder().token("sk-xxx")
215 | .proxyHost("127.0.0.1")
216 | .proxyPort(7890)
217 | .build();
218 | OpenAiProxyService openAiProxyService = new OpenAiProxyService(properties);
219 | // 创建微调
220 | FineTuneRequest fineTuneRequest = FineTuneRequest.builder().trainingFile("").build();
221 | System.out.println("创建微调信息:" + openAiProxyService.createFineTune(fineTuneRequest));
222 | // 创建微调完成
223 | CompletionRequest completionRequest = CompletionRequest.builder().build();
224 | System.out.println("创建微调完成信息:" + openAiProxyService.createFineTuneCompletion(completionRequest));
225 | // 获取微调列表
226 | System.out.println("获取微调列表:" + openAiProxyService.listFineTunes());
227 | // 获取微调信息
228 | System.out.println("获取微调信息:" + openAiProxyService.retrieveFineTune(""));
229 | // 取消微调
230 | System.out.println("取消微调信息:" + openAiProxyService.cancelFineTune(""));
231 | // 列出微调事件
232 | System.out.println("列出微调事件:" + openAiProxyService.listFineTuneEvents(""));
233 | // 删除微调
234 | System.out.println("删除微调信息:" + openAiProxyService.deleteFineTune(""));
235 | }
236 |
237 | @Test
238 | public void moderation() {
239 | ChatGPTProperties properties = ChatGPTProperties.builder().token("sk-xxx")
240 | .proxyHost("127.0.0.1")
241 | .proxyPort(7890)
242 | .build();
243 | OpenAiProxyService openAiProxyService = new OpenAiProxyService(properties);
244 | // 创建moderation
245 | ModerationRequest moderationRequest = ModerationRequest.builder().input("I want to kill them.").build();
246 | System.out.println("创建moderation信息:" + openAiProxyService.createModeration(moderationRequest));
247 | }
248 |
249 | @Test
250 | public void baseUrl() {
251 | ChatGPTProperties properties = ChatGPTProperties.builder().token("sk-xxx")
252 | // 自定义baseUrl
253 | .baseUrl("https://openai.api2d.net/")
254 | .build();
255 | OpenAiProxyService openAiProxyService = new OpenAiProxyService(properties);
256 | System.out.println("models列表:" + openAiProxyService.listModels());
257 | }
258 |
259 | @Test
260 | public void systemPromptTest() {
261 | ChatGPTProperties properties = ChatGPTProperties.builder().token("sk-xxx")
262 | // 自定义baseUrl
263 | .proxyHost("127.0.0.1")
264 | .proxyPort(7890)
265 | .build();
266 | OpenAiProxyService openAiProxyService = new OpenAiProxyService(properties);
267 | System.out.println("初始系统级提示信息为:" + openAiProxyService.getSystemPrompt());
268 | openAiProxyService.setSystemPrompt("我是一个Java开发工程师,所有的代码请求都请用Java给我生成。");
269 | openAiProxyService.createStreamChatCompletion("写一个迭代器模式的代码");
270 | // System.out.println("当前的系统级信息提示为:" + openAiProxyService.getSystemPrompt());
271 | // 清理系统级提示信息
272 | // openAiProxyService.cleanUpSystemPrompt();
273 | // System.out.println("清理后的系统级提示信息为:" + openAiProxyService.getSystemPrompt());
274 | }
275 |
276 | @Test
277 | public void countTokensTest() {
278 | ChatGPTProperties properties = ChatGPTProperties.builder().token("sk-xxx")
279 | // 自定义baseUrl
280 | .proxyHost("127.0.0.1")
281 | .proxyPort(7890)
282 | .build();
283 | OpenAiProxyService openAiProxyService = new OpenAiProxyService(properties);
284 | String text = "Hello World!";
285 |
286 | System.out.println("当前输入文字使用模型[gpt-3.5-turbo] token总数为:" + openAiProxyService.countTokens(text));
287 | ModelType modelType = ModelType.GPT_4_32K;
288 | // 实际上单就计算的token的数目上来说3.5和4是一样的
289 | System.out.println("当前输入文字使用模型[gpt-4-32k] token总数为:" + openAiProxyService.countTokens(text, modelType));
290 | }
291 |
292 | @Test
293 | public void alterTokensTest() {
294 | ChatGPTProperties properties = ChatGPTProperties.builder().token("sk-xxx1")
295 | // 自定义baseUrl
296 | .proxyHost("127.0.0.1")
297 | .proxyPort(7890)
298 | .alterTokens(Arrays.asList("sk-xxx2", "sk-xxx3"))
299 | .tokenStrategyImpl(SelectSecondStrategy.class)
300 | .build();
301 | OpenAiProxyService openAiProxyService = new OpenAiProxyService(properties);
302 | System.out.println("models列表:" + openAiProxyService.listModels());
303 | }
304 |
305 | /* @Test
306 | public void okHttpClient() {
307 | ChatGPTProperties properties = ChatGPTProperties.builder().token("sk-xxx")
308 | .proxyHost("127.0.0.1")
309 | .proxyPort(7890)
310 | .build();
311 | Dispatcher dispatcher = new Dispatcher();
312 | dispatcher.setMaxRequests(100);
313 | dispatcher.setMaxRequestsPerHost(10);
314 | // 自定义okHttpClient
315 | OkHttpClient okHttpClient = new OkHttpClient.Builder()
316 | .addInterceptor(new AuthenticationInterceptor(properties.getToken()))
317 | .connectionPool(new ConnectionPool(100, 10, TimeUnit.SECONDS))
318 | .readTimeout(Duration.ZERO.toMillis(), TimeUnit.MILLISECONDS)
319 | .connectTimeout(Duration.ZERO.toMillis(), TimeUnit.MILLISECONDS)
320 | .hostnameVerifier((hostname, session) -> true)
321 | .proxy(new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(properties.getProxyHost(), properties.getProxyPort())))
322 | .proxyAuthenticator((route, response) -> {
323 | String credential = Credentials.basic("proxyUsername", "proxyPassword");
324 | return response.request().newBuilder()
325 | .header("Proxy-Authorization", credential)
326 | .build();
327 | })
328 | .dispatcher(dispatcher)
329 | .build();
330 | // 下面的openAiProxyService使用自定义的okHttpClient
331 | OpenAiProxyService openAiProxyService = new OpenAiProxyService(properties, okHttpClient);
332 | System.out.println("models列表:" + openAiProxyService.listModels());
333 | }*/
334 | }
335 |
--------------------------------------------------------------------------------
/src/main/java/com/asleepyfish/strategy/SelectSecondStrategy.java:
--------------------------------------------------------------------------------
1 | package com.asleepyfish.strategy;
2 |
3 | import io.github.asleepyfish.enums.exception.ChatGPTErrorEnum;
4 | import io.github.asleepyfish.exception.ChatGPTException;
5 | import io.github.asleepyfish.strategy.TokenStrategy;
6 | import org.springframework.util.CollectionUtils;
7 |
8 | import java.util.List;
9 |
10 | /**
11 | * @Author: asleepyfish
12 | * @Date: 2023/12/28 14:57
13 | * @Description: SelectSecondStrategy
14 | */
15 | public class SelectSecondStrategy implements TokenStrategy {
16 |
17 | @Override
18 | public String getToken(List tokens) {
19 | if (!CollectionUtils.isEmpty(tokens) && tokens.size() > 1) {
20 | return tokens.get(1);
21 | }
22 | throw new ChatGPTException(ChatGPTErrorEnum.NO_AVAILABLE_TOKEN_ERROR);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | chatgpt:
2 | token: sk-xxx1 #必填
3 | proxy-host: 127.0.0.1 #需要代理时必填
4 | proxy-port: 7890 #需要代理时必填
5 | # model: text-davinci-003 #可选
6 | # chat-model: gpt-3.5-turbo #可选
7 | # retries: 10 #可选,默认为5
8 | # session-expiration-time: 30 #可选,不填则会话永不过期
9 | # base-url: https://apps.ichati.cn/e4713307-eb91-4598-a88c-eff6eeccexxx/ #可选,默认为https://api.openai.com/,请记住务必以/结尾
10 | # token-strategy-impl: io.github.asleepyfish.strategy.RandomTokenStrategy #可选,默认为RandomTokenStrategy(随机)
11 | # alter-tokens: #可选,备选tokens,可以和token-strategy-impl结合,每次请求按策略分配token
12 | # - sk-xxx2
13 | # - sk-xxx3
14 |
--------------------------------------------------------------------------------
/src/main/resources/audio/想象之中-许嵩.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/asleepyfish/chatgpt-demo/5a67e718d8f0d11dc86030701b788750a7d1a8b1/src/main/resources/audio/想象之中-许嵩.mp3
--------------------------------------------------------------------------------
/src/main/resources/image/img.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/asleepyfish/chatgpt-demo/5a67e718d8f0d11dc86030701b788750a7d1a8b1/src/main/resources/image/img.png
--------------------------------------------------------------------------------
/src/main/resources/image/mask.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/asleepyfish/chatgpt-demo/5a67e718d8f0d11dc86030701b788750a7d1a8b1/src/main/resources/image/mask.png
--------------------------------------------------------------------------------
/src/test/java/com/asleepyfish/ChatgptDemoApplicationTests.java:
--------------------------------------------------------------------------------
1 | package com.asleepyfish;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.springframework.boot.test.context.SpringBootTest;
5 |
6 | @SpringBootTest
7 | class ChatgptDemoApplicationTests {
8 |
9 | @Test
10 | void contextLoads() {
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/src/test/java/com/asleepyfish/test/ChatGPTTest.java:
--------------------------------------------------------------------------------
1 | package com.asleepyfish.test;
2 |
3 | import io.github.asleepyfish.util.OpenAiUtils;
4 | import org.junit.jupiter.api.Test;
5 | import org.springframework.boot.test.context.SpringBootTest;
6 |
7 | /**
8 | * @Author: asleepyfish
9 | * @Date: 2023/2/14 10:18
10 | * @Description: ChatGPTTest
11 | */
12 | @SpringBootTest
13 | public class ChatGPTTest {
14 | @Test
15 | public void testChatGPT() {
16 | OpenAiUtils.createCompletion("世界上最高的山峰是什么?").forEach(System.out::println);
17 | }
18 |
19 | @Test
20 | public void testGenerateImg() {
21 | OpenAiUtils.createImage("英短").forEach(System.out::println);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------