diff --git a/README.md b/README.md index 498036e..196c5e8 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,15 @@ [中文版文档](https://vayne.cc/2022/12/17/chatgpt-spring-boot-starter) # chatgpt-spring-boot-starter -Use chatgpt in springboot project easily. -This starter is based on Openai Official Apis. +This starter is based on OpenAi Official Apis. You can use chatgpt in springboot project easily. +## Functions: ++ Chat + + You can chat with ChatGPT using many models. Also, multi message is supported, so you can take a series of messages (including the conversation history) as input , and get a response message. + ++ Image generation + + Given a prompt and get generated image(s). ## Usage ### 1.Add maven dependency. @@ -12,7 +19,7 @@ This starter is based on Openai Official Apis. io.github.flashvayne chatgpt-spring-boot-starter - 1.0.2 + 1.0.3 ``` ### 2.Set chatgpt properties in your application.yml @@ -20,21 +27,13 @@ This starter is based on Openai Official Apis. ```yml chatgpt: api-key: xxxxxxxxxxx #api-key. It can be generated here https://platform.openai.com/account/api-keys -# some properties as below have default values. For descriptions of these fields, please refer to https://platform.openai.com/docs/api-reference/completions/create and https://platform.openai.com/docs/api-reference/chat/create -# url: https://api.openai.com/v1/completions -# model: text-davinci-003 -# max-tokens: 500 - temperature: 1.0 -# top-p: 1.0 - multi: -# url: https://api.openai.com/v1/chat/completions -# model: gpt-3.5-turbo -# max-tokens: 500 - temperature: 1.0 -# top-p: 1.0 +# some more properties(model,max-tokens...etc.) have default values. Also you can config them here. ``` -### 3.Inject bean ChatgptService anywhere you require it, and invoke its method to send message to chatgpt and get the response. -#### 3.1 Single message +### 3.Inject bean ChatgptService anywhere you require it, and invoke its methods. +#### 3.1 Chat + +##### 3.1.1 Single message + ```java @Autowired private ChatgptService chatgptService; @@ -49,7 +48,7 @@ public void test2(){ System.out.print(responseMessage); //I'm doing well, thank you. How about you? } ``` -#### 3.2 Multi message. You can take a series of messages (including the conversation history) as input , and return a response message as output. +##### 3.1.2 Multi message. You can take a series of messages (including the conversation history) as input , and return a response message as output. ```java @Autowired private ChatgptService chatgptService; @@ -64,12 +63,29 @@ public void testMultiChat(){ System.out.print(responseMessage); //The 2020 World Series was played at Globe Life Field in Arlington, Texas. } ``` -Messages must be an array of message objects, where each object has a role (either "system", "user", or "assistant") and content (the content of the message). Conversations can be as short as 1 message or fill many pages. ++ Tips: + + Messages must be an array of message objects, where each object has a role (either "system", "user", or "assistant") and content (the content of the message). Conversations can be as short as 1 message or fill many pages. + + The system message helps set the behavior of the assistant. In the example above, the assistant was instructed with "You are a helpful assistant." + + The user messages help instruct the assistant. They can be generated by the end users of an application, or set by a developer as an instruction. + + The assistant messages help store prior responses. They can also be written by a developer to help give examples of desired behavior. + For more details, please refer to [chat format](https://platform.openai.com/docs/guides/chat/introduction) + -+ The system message helps set the behavior of the assistant. In the example above, the assistant was instructed with "You are a helpful assistant." -+ The user messages help instruct the assistant. They can be generated by the end users of an application, or set by a developer as an instruction. -+ The assistant messages help store prior responses. They can also be written by a developer to help give examples of desired behavior. -For more details, please refer to [chat format](https://platform.openai.com/docs/guides/chat/introduction) +#### 3.2 Image generation +```java +@Autowired +private ChatgptService chatgptService; + +public void testImage(){ + String imageUrl = chatgptService.imageGenerate("A cute baby sea otter"); + System.out.print(imageUrl); //https://oaidalleapip....... +} + +public void testImageList(){ + List images = chatgptService.imageGenerate("A cute baby sea otter", 2, ImageSize.SMALL, ImageFormat.URL); + System.out.print(images.toString());//["https://oaidalleapipr.....ZwA%3D","https://oaidalleapipr....RE0%3D"] +} +``` ## Demo project: [demo-chatgpt-spring-boot-starter](https://github.com/flashvayne/demo-chatgpt-spring-boot-starter) diff --git a/pom.xml b/pom.xml index 809d8a2..68ddeba 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.github.flashvayne chatgpt-spring-boot-starter - 1.0.2 + 1.0.3 chatgpt-spring-boot-starter a starter to use chatgpt in springboot project easily @@ -58,7 +58,7 @@ org.projectlombok lombok provided - 1.18.24 + 1.18.26 @@ -79,7 +79,7 @@ - + diff --git a/src/main/java/io/github/flashvayne/chatgpt/dto/image/ImageData.java b/src/main/java/io/github/flashvayne/chatgpt/dto/image/ImageData.java new file mode 100644 index 0000000..05b03e1 --- /dev/null +++ b/src/main/java/io/github/flashvayne/chatgpt/dto/image/ImageData.java @@ -0,0 +1,12 @@ +package io.github.flashvayne.chatgpt.dto.image; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +@Data +public class ImageData { + private String url; + + @JsonProperty("b64_json") + private String b64Json; +} diff --git a/src/main/java/io/github/flashvayne/chatgpt/dto/image/ImageFormat.java b/src/main/java/io/github/flashvayne/chatgpt/dto/image/ImageFormat.java new file mode 100644 index 0000000..984b5cb --- /dev/null +++ b/src/main/java/io/github/flashvayne/chatgpt/dto/image/ImageFormat.java @@ -0,0 +1,17 @@ +package io.github.flashvayne.chatgpt.dto.image; + +public enum ImageFormat { + + URL("url"),BASE64("b64_json"); + + private final String format; + + ImageFormat(String format){ + this.format = format; + } + + public String getFormat(){ + return format; + } + +} diff --git a/src/main/java/io/github/flashvayne/chatgpt/dto/image/ImageRequest.java b/src/main/java/io/github/flashvayne/chatgpt/dto/image/ImageRequest.java new file mode 100644 index 0000000..69c0ccb --- /dev/null +++ b/src/main/java/io/github/flashvayne/chatgpt/dto/image/ImageRequest.java @@ -0,0 +1,28 @@ +package io.github.flashvayne.chatgpt.dto.image; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * ImageRequest is used to construct request body. + * For descriptions of all fields, please refer to Create image + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@JsonInclude(JsonInclude.Include.NON_NULL) +public class ImageRequest { + + private String prompt; + private Integer n; + private String size; + + @JsonProperty("response_format") + private String responseFormat; + + private String user; + +} diff --git a/src/main/java/io/github/flashvayne/chatgpt/dto/image/ImageResponse.java b/src/main/java/io/github/flashvayne/chatgpt/dto/image/ImageResponse.java new file mode 100644 index 0000000..0af4448 --- /dev/null +++ b/src/main/java/io/github/flashvayne/chatgpt/dto/image/ImageResponse.java @@ -0,0 +1,18 @@ +package io.github.flashvayne.chatgpt.dto.image; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Date; +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class ImageResponse { + private Date created; + + private List data; + +} diff --git a/src/main/java/io/github/flashvayne/chatgpt/dto/image/ImageSize.java b/src/main/java/io/github/flashvayne/chatgpt/dto/image/ImageSize.java new file mode 100644 index 0000000..a1fd060 --- /dev/null +++ b/src/main/java/io/github/flashvayne/chatgpt/dto/image/ImageSize.java @@ -0,0 +1,17 @@ +package io.github.flashvayne.chatgpt.dto.image; + +public enum ImageSize { + + SMALL("256x256"), MEDIUM("512x512"), LARGE("1024x1024"); + + private final String size; + + ImageSize(String size) { + this.size = size; + } + + public String getSize() { + return size; + } + +} diff --git a/src/main/java/io/github/flashvayne/chatgpt/property/ChatgptProperties.java b/src/main/java/io/github/flashvayne/chatgpt/property/ChatgptProperties.java index 750e01c..c5fff98 100644 --- a/src/main/java/io/github/flashvayne/chatgpt/property/ChatgptProperties.java +++ b/src/main/java/io/github/flashvayne/chatgpt/property/ChatgptProperties.java @@ -15,14 +15,17 @@ public class ChatgptProperties { private Integer maxTokens = 500; - private Double temperature = 0.0; + private Double temperature = 1.0; private Double topP = 1.0; private MultiChatProperties multi; + private ImageProperties image; + public ChatgptProperties() { this.multi = new MultiChatProperties(); + this.image = new ImageProperties(); } public String getApiKey() { @@ -80,4 +83,12 @@ public MultiChatProperties getMulti() { public void setMulti(MultiChatProperties multi) { this.multi = multi; } + + public ImageProperties getImage() { + return image; + } + + public void setImage(ImageProperties image) { + this.image = image; + } } diff --git a/src/main/java/io/github/flashvayne/chatgpt/property/ImageProperties.java b/src/main/java/io/github/flashvayne/chatgpt/property/ImageProperties.java new file mode 100644 index 0000000..3dc401b --- /dev/null +++ b/src/main/java/io/github/flashvayne/chatgpt/property/ImageProperties.java @@ -0,0 +1,14 @@ +package io.github.flashvayne.chatgpt.property; + +public class ImageProperties { + + private String url = "https://api.openai.com/v1/images/generations"; + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } +} diff --git a/src/main/java/io/github/flashvayne/chatgpt/property/MultiChatProperties.java b/src/main/java/io/github/flashvayne/chatgpt/property/MultiChatProperties.java index b541df7..6c24fe1 100644 --- a/src/main/java/io/github/flashvayne/chatgpt/property/MultiChatProperties.java +++ b/src/main/java/io/github/flashvayne/chatgpt/property/MultiChatProperties.java @@ -8,7 +8,7 @@ public class MultiChatProperties { private Integer maxTokens = 500; - private Double temperature = 0.0; + private Double temperature = 1.0; private Double topP = 1.0; diff --git a/src/main/java/io/github/flashvayne/chatgpt/service/ChatgptService.java b/src/main/java/io/github/flashvayne/chatgpt/service/ChatgptService.java index 50c5c4b..6c67590 100644 --- a/src/main/java/io/github/flashvayne/chatgpt/service/ChatgptService.java +++ b/src/main/java/io/github/flashvayne/chatgpt/service/ChatgptService.java @@ -5,6 +5,10 @@ import io.github.flashvayne.chatgpt.dto.chat.MultiChatMessage; import io.github.flashvayne.chatgpt.dto.chat.MultiChatRequest; import io.github.flashvayne.chatgpt.dto.chat.MultiChatResponse; +import io.github.flashvayne.chatgpt.dto.image.ImageFormat; +import io.github.flashvayne.chatgpt.dto.image.ImageRequest; +import io.github.flashvayne.chatgpt.dto.image.ImageResponse; +import io.github.flashvayne.chatgpt.dto.image.ImageSize; import java.util.List; @@ -17,4 +21,22 @@ public interface ChatgptService { String multiChat(List messages); MultiChatResponse multiChatRequest(MultiChatRequest multiChatRequest); + + /** + * @param prompt A text description of the desired image(s). The maximum length is 1000 characters. + * @return generated image url + */ + String imageGenerate(String prompt); + + /** + * @param prompt A text description of the desired image(s). The maximum length is 1000 characters. + * @param n The number of images to generate. Must be between 1 and 10. + * @param size The size of the generated images. Must be one of ImageFormat.SMALL("256x256"), ImageFormat.MEDIUM("512x512"), ImageFormat.LARGE("1024x1024"). + * @param format The format in which the generated images are returned. Must be one of ImageFormat.URL("url"), ImageFormat.BASE64("b64_json"). + * @return image url/base64 list + */ + List imageGenerate(String prompt, Integer n, ImageSize size, ImageFormat format); + + ImageResponse imageGenerateRequest(ImageRequest imageRequest); + } diff --git a/src/main/java/io/github/flashvayne/chatgpt/service/impl/DefaultChatgptService.java b/src/main/java/io/github/flashvayne/chatgpt/service/impl/DefaultChatgptService.java index 26fdd23..df4f95f 100644 --- a/src/main/java/io/github/flashvayne/chatgpt/service/impl/DefaultChatgptService.java +++ b/src/main/java/io/github/flashvayne/chatgpt/service/impl/DefaultChatgptService.java @@ -4,6 +4,7 @@ import io.github.flashvayne.chatgpt.dto.chat.MultiChatMessage; import io.github.flashvayne.chatgpt.dto.chat.MultiChatRequest; import io.github.flashvayne.chatgpt.dto.chat.MultiChatResponse; +import io.github.flashvayne.chatgpt.dto.image.*; import io.github.flashvayne.chatgpt.exception.ChatgptException; import io.github.flashvayne.chatgpt.property.ChatgptProperties; import io.github.flashvayne.chatgpt.dto.ChatResponse; @@ -16,6 +17,7 @@ import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; +import java.util.ArrayList; import java.util.List; @@ -36,8 +38,7 @@ public DefaultChatgptService(ChatgptProperties chatgptProperties) { @Override public String sendMessage(String message) { - ChatRequest chatRequest = new ChatRequest(chatgptProperties.getModel(), message, - chatgptProperties.getMaxTokens(), chatgptProperties.getTemperature(), chatgptProperties.getTopP()); + ChatRequest chatRequest = new ChatRequest(chatgptProperties.getModel(), message, chatgptProperties.getMaxTokens(), chatgptProperties.getTemperature(), chatgptProperties.getTopP()); ChatResponse chatResponse = this.getResponse(this.buildHttpEntity(chatRequest), ChatResponse.class, chatgptProperties.getUrl()); try { return chatResponse.getChoices().get(0).getText(); @@ -54,8 +55,7 @@ public ChatResponse sendChatRequest(ChatRequest chatRequest) { @Override public String multiChat(List messages) { - MultiChatRequest multiChatRequest = new MultiChatRequest(chatgptProperties.getMulti().getModel(), messages, - chatgptProperties.getMulti().getMaxTokens(), chatgptProperties.getMulti().getTemperature(), chatgptProperties.getMulti().getTopP()); + MultiChatRequest multiChatRequest = new MultiChatRequest(chatgptProperties.getMulti().getModel(), messages, chatgptProperties.getMulti().getMaxTokens(), chatgptProperties.getMulti().getTemperature(), chatgptProperties.getMulti().getTopP()); MultiChatResponse multiChatResponse = this.getResponse(this.buildHttpEntity(multiChatRequest), MultiChatResponse.class, chatgptProperties.getMulti().getUrl()); try { return multiChatResponse.getChoices().get(0).getMessage().getContent(); @@ -70,6 +70,43 @@ public MultiChatResponse multiChatRequest(MultiChatRequest multiChatRequest) { return this.getResponse(this.buildHttpEntity(multiChatRequest), MultiChatResponse.class, chatgptProperties.getMulti().getUrl()); } + @Override + public String imageGenerate(String prompt) { + ImageRequest imageRequest = new ImageRequest(prompt, null, null, null, null); + ImageResponse imageResponse = this.getResponse(this.buildHttpEntity(imageRequest), ImageResponse.class, chatgptProperties.getImage().getUrl()); + try { + return imageResponse.getData().get(0).getUrl(); + } catch (Exception e) { + log.error("parse image url error", e); + throw e; + } + } + + @Override + public List imageGenerate(String prompt, Integer n, ImageSize size, ImageFormat format) { + ImageRequest imageRequest = new ImageRequest(prompt, n, size.getSize(), format.getFormat(), null); + ImageResponse imageResponse = this.getResponse(this.buildHttpEntity(imageRequest), ImageResponse.class, chatgptProperties.getImage().getUrl()); + try { + List list = new ArrayList<>(); + imageResponse.getData().forEach(imageData -> { + if (format.equals(ImageFormat.URL)) { + list.add(imageData.getUrl()); + } else { + list.add(imageData.getB64Json()); + } + }); + return list; + } catch (Exception e) { + log.error("parse image url error", e); + throw e; + } + } + + @Override + public ImageResponse imageGenerateRequest(ImageRequest imageRequest) { + return this.getResponse(this.buildHttpEntity(imageRequest), ImageResponse.class, chatgptProperties.getImage().getUrl()); + } + protected HttpEntity buildHttpEntity(T request) { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.parseMediaType("application/json; charset=UTF-8")); diff --git a/src/main/resources/META-INF/spring-configuration-metadata.json b/src/main/resources/META-INF/spring-configuration-metadata.json index 2160487..d6e8ea5 100644 --- a/src/main/resources/META-INF/spring-configuration-metadata.json +++ b/src/main/resources/META-INF/spring-configuration-metadata.json @@ -12,7 +12,8 @@ "type": "java.lang.String", "sourceType": "io.github.flashvayne.chatgpt.property.ChatgptProperties", "defaultValue": "", - "description": "apiKey of chatgpt. It can be generated in this link https://platform.openai.com/account/api-keys" + "description": "apiKey of chatgpt. It can be generated in this link https://platform.openai.com/account/api-keys", + "required": true }, { "name": "chatgpt.model", @@ -38,7 +39,7 @@ "name": "chatgpt.temperature", "type": "java.lang.Double", "sourceType": "io.github.flashvayne.chatgpt.property.ChatgptProperties", - "defaultValue": "0.0", + "defaultValue": "1.0", "description": "What sampling temperature to use. Higher values means the model will take more risks. Try 0.9 for more creative applications, and 0 (argmax sampling) for ones with a well-defined answer.We generally recommend altering this or top_p but not both." },{ "name": "chatgpt.top-p", @@ -59,7 +60,7 @@ "name": "chatgpt.multi.url", "type": "java.lang.String", "sourceType": "io.github.flashvayne.chatgpt.property.MultiChatProperties", - "defaultValue": "https://api.openai.com/v1/completions", + "defaultValue": "https://api.openai.com/v1/chat/completions", "description": "The request url." }, { @@ -72,7 +73,7 @@ "name": "chatgpt.multi.temperature", "type": "java.lang.Double", "sourceType": "io.github.flashvayne.chatgpt.property.MultiChatProperties", - "defaultValue": "0.0", + "defaultValue": "1.0", "description": "What sampling temperature to use. Higher values means the model will take more risks. Try 0.9 for more creative applications, and 0 (argmax sampling) for ones with a well-defined answer.We generally recommend altering this or top_p but not both." },{ "name": "chatgpt.multi.top-p", @@ -80,6 +81,12 @@ "sourceType": "io.github.flashvayne.chatgpt.property.MultiChatProperties", "defaultValue": "1.0", "description": "An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.We generally recommend altering this or temperature but not both." + },{ + "name": "chatgpt.image.url", + "type": "java.lang.String", + "sourceType": "io.github.flashvayne.chatgpt.property.ImageProperties", + "defaultValue": "https://api.openai.com/v1/images/generations", + "description": "The request url." } ], "hints": []