|
|
@ -1,31 +1,11 @@
|
|
|
|
package cn.lige2333.qrdouble.chatgpt.service;
|
|
|
|
package cn.lige2333.ligeai.chatgpt.service;
|
|
|
|
|
|
|
|
|
|
|
|
import cn.hutool.core.collection.CollUtil;
|
|
|
|
|
|
|
|
import cn.hutool.core.util.StrUtil;
|
|
|
|
|
|
|
|
import cn.hutool.json.JSONUtil;
|
|
|
|
|
|
|
|
import cn.lige2333.qrdouble.security.entity.SysUser;
|
|
|
|
|
|
|
|
import com.alibaba.fastjson.JSON;
|
|
|
|
|
|
|
|
import com.alibaba.fastjson.JSONObject;
|
|
|
|
|
|
|
|
import com.unfbx.chatgpt.OpenAiClient;
|
|
|
|
|
|
|
|
import com.unfbx.chatgpt.OpenAiStreamClient;
|
|
|
|
|
|
|
|
import com.unfbx.chatgpt.entity.chat.*;
|
|
|
|
|
|
|
|
import com.unfbx.chatgpt.entity.common.Choice;
|
|
|
|
|
|
|
|
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.sse.ConsoleEventSourceListener;
|
|
|
|
|
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
|
import org.apache.logging.log4j.util.Strings;
|
|
|
|
|
|
|
|
import org.jetbrains.annotations.NotNull;
|
|
|
|
|
|
|
|
import org.springframework.ai.chat.client.ChatClient;
|
|
|
|
import org.springframework.ai.chat.client.ChatClient;
|
|
|
|
import org.springframework.ai.chat.client.advisor.AbstractChatMemoryAdvisor;
|
|
|
|
import org.springframework.ai.chat.client.advisor.AbstractChatMemoryAdvisor;
|
|
|
|
import org.springframework.ai.chat.client.advisor.PromptChatMemoryAdvisor;
|
|
|
|
import org.springframework.ai.chat.client.advisor.PromptChatMemoryAdvisor;
|
|
|
|
import org.springframework.ai.chat.client.advisor.QuestionAnswerAdvisor;
|
|
|
|
|
|
|
|
import org.springframework.ai.chat.memory.ChatMemory;
|
|
|
|
import org.springframework.ai.chat.memory.ChatMemory;
|
|
|
|
import org.springframework.ai.chat.model.ChatResponse;
|
|
|
|
import org.springframework.ai.chat.model.ChatResponse;
|
|
|
|
import org.springframework.ai.openai.api.OpenAiApi;
|
|
|
|
|
|
|
|
import org.springframework.ai.vectorstore.SearchRequest;
|
|
|
|
|
|
|
|
import org.springframework.ai.vectorstore.VectorStore;
|
|
|
|
|
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
import org.springframework.web.bind.annotation.CrossOrigin;
|
|
|
|
import org.springframework.web.bind.annotation.CrossOrigin;
|
|
|
@ -41,12 +21,6 @@ import java.util.*;
|
|
|
|
@CrossOrigin
|
|
|
|
@CrossOrigin
|
|
|
|
public class ChatGptService {
|
|
|
|
public class ChatGptService {
|
|
|
|
|
|
|
|
|
|
|
|
@Autowired
|
|
|
|
|
|
|
|
private OpenAiClient openAiClient;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Autowired
|
|
|
|
|
|
|
|
private OpenAiStreamClient openAiStreamClient;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private final ChatClient chatClient;
|
|
|
|
private final ChatClient chatClient;
|
|
|
|
|
|
|
|
|
|
|
|
public ChatGptService(ChatClient.Builder chatClientBuilder, ChatMemory chatMemory) {
|
|
|
|
public ChatGptService(ChatClient.Builder chatClientBuilder, ChatMemory chatMemory) {
|
|
|
@ -74,10 +48,7 @@ public class ChatGptService {
|
|
|
|
.build();
|
|
|
|
.build();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public Flux<Map<String,String>> getChatGptResStream(String input, SysUser sysUser) throws IOException {
|
|
|
|
public Flux<Map<String,String>> getChatGptResStream(String input) throws IOException {
|
|
|
|
if(Objects.isNull(sysUser)){
|
|
|
|
|
|
|
|
throw new RuntimeException("用户信息获取失败!");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
String id = UUID.randomUUID().toString();
|
|
|
|
String id = UUID.randomUUID().toString();
|
|
|
|
Map<String, String> finishMap = new HashMap<>();
|
|
|
|
Map<String, String> finishMap = new HashMap<>();
|
|
|
|
finishMap.put("content", "[DONE]");
|
|
|
|
finishMap.put("content", "[DONE]");
|
|
|
@ -96,163 +67,4 @@ public class ChatGptService {
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public ChatCompletionResponse getCompletionRes(ChatCompletion chatCompletion) {
|
|
|
|
|
|
|
|
List<Message> messages = chatCompletion.getMessages();
|
|
|
|
|
|
|
|
ChatCompletionResponse chatCompletionResponse = new ChatCompletionResponse();
|
|
|
|
|
|
|
|
if (CollUtil.isNotEmpty(messages)) {
|
|
|
|
|
|
|
|
Message message = messages.get(messages.size() - 1);
|
|
|
|
|
|
|
|
ChatResponse currentDate = this.chatClient.prompt()
|
|
|
|
|
|
|
|
.user(message.getContent())
|
|
|
|
|
|
|
|
.system(promptSystemSpec -> promptSystemSpec.param("current_date", LocalDateTime.now().toString()))
|
|
|
|
|
|
|
|
.advisors(advisorSpec -> advisorSpec.param(AbstractChatMemoryAdvisor.CHAT_MEMORY_RETRIEVE_SIZE_KEY, 100))
|
|
|
|
|
|
|
|
.call().chatResponse();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
chatCompletionResponse.setId(currentDate.getMetadata().getId());
|
|
|
|
|
|
|
|
ChatChoice choice = new ChatChoice();
|
|
|
|
|
|
|
|
choice.setIndex(0);
|
|
|
|
|
|
|
|
choice.setMessage(Message.builder().content(currentDate.getResult().getOutput().getContent()).role(Message.Role.ASSISTANT).build());
|
|
|
|
|
|
|
|
choice.setFinishReason(currentDate.getResult().getMetadata().getFinishReason());
|
|
|
|
|
|
|
|
chatCompletionResponse.setChoices(Arrays.asList(choice));
|
|
|
|
|
|
|
|
return chatCompletionResponse;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return chatCompletionResponse;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
public Flux<ChatCompletionResponse> getStreamingRes(ChatCompletion chatCompletion) {
|
|
|
|
|
|
|
|
List<Message> messages = chatCompletion.getMessages();
|
|
|
|
|
|
|
|
if(CollUtil.isNotEmpty(messages)){
|
|
|
|
|
|
|
|
Message message = messages.get(messages.size() - 1);
|
|
|
|
|
|
|
|
Flux<ChatCompletionResponse> result = this.chatClient.prompt()
|
|
|
|
|
|
|
|
.user(message.getContent())
|
|
|
|
|
|
|
|
.system(promptSystemSpec -> promptSystemSpec.param("current_date", LocalDateTime.now().toString()))
|
|
|
|
|
|
|
|
.advisors(advisorSpec -> advisorSpec.param(AbstractChatMemoryAdvisor.CHAT_MEMORY_RETRIEVE_SIZE_KEY, 100))
|
|
|
|
|
|
|
|
.stream().chatResponse().map(res -> {
|
|
|
|
|
|
|
|
ChatCompletionResponse chatCompletionResponse = new ChatCompletionResponse();
|
|
|
|
|
|
|
|
chatCompletionResponse.setId(res.getMetadata().getId());
|
|
|
|
|
|
|
|
ChatChoice choice = new ChatChoice();
|
|
|
|
|
|
|
|
choice.setIndex(0);
|
|
|
|
|
|
|
|
choice.setDelta(Message.builder().content(res.getResult().getOutput().getContent()).role(Message.Role.ASSISTANT).build());
|
|
|
|
|
|
|
|
choice.setFinishReason(res.getResult().getMetadata().getFinishReason());
|
|
|
|
|
|
|
|
chatCompletionResponse.setChoices(Arrays.asList(choice));
|
|
|
|
|
|
|
|
return chatCompletionResponse;
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return Flux.just(new ChatCompletionResponse());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public String getChatGptRes(String input, SysUser sysUser) {
|
|
|
|
|
|
|
|
List<Message> messages = getMessages(input, sysUser);
|
|
|
|
|
|
|
|
ChatCompletion chatCompletion = ChatCompletion.builder().messages(messages).model(ChatCompletion.Model.GPT_4.getName()).build();
|
|
|
|
|
|
|
|
ChatCompletionResponse chatCompletionResponse = openAiClient.chatCompletion(chatCompletion);
|
|
|
|
|
|
|
|
return chatCompletionResponse.getChoices().get(0).getMessage().getContent().replaceAll("\r|\n", "");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private List<Message> getMessages(String input, SysUser sysUser) {
|
|
|
|
|
|
|
|
String messageContext = (String) LocalCache.CACHE.get(sysUser.getUserName());
|
|
|
|
|
|
|
|
List<Message> messages = new ArrayList<>();
|
|
|
|
|
|
|
|
if (StrUtil.isNotBlank(messageContext)) {
|
|
|
|
|
|
|
|
messages = JSONUtil.toList(messageContext, Message.class);
|
|
|
|
|
|
|
|
if (messages.size() >= 10) {
|
|
|
|
|
|
|
|
messages = messages.subList(1, 10);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Message currentMessage = Message.builder().content(input).role(Message.Role.USER).build();
|
|
|
|
|
|
|
|
messages.add(currentMessage);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
Message currentMessage = Message.builder().content(input).role(Message.Role.USER).build();
|
|
|
|
|
|
|
|
messages.add(currentMessage);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return messages;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void pictureChat(String text,String imgUrl, SysUser sysUser) {
|
|
|
|
|
|
|
|
Content textContent = Content.builder().text(text).type(Content.Type.TEXT.getName()).build();
|
|
|
|
|
|
|
|
ImageUrl imageUrl = ImageUrl.builder().url(imgUrl).build();
|
|
|
|
|
|
|
|
Content imageContent = Content.builder().imageUrl(imageUrl).type(Content.Type.IMAGE_URL.getName()).build();
|
|
|
|
|
|
|
|
List<Content> contentList = new ArrayList<>();
|
|
|
|
|
|
|
|
contentList.add(textContent);
|
|
|
|
|
|
|
|
contentList.add(imageContent);
|
|
|
|
|
|
|
|
MessagePicture message = MessagePicture.builder().role(Message.Role.USER).content(contentList).build();
|
|
|
|
|
|
|
|
ChatCompletionWithPicture chatCompletion = ChatCompletionWithPicture
|
|
|
|
|
|
|
|
.builder()
|
|
|
|
|
|
|
|
.messages(Collections.singletonList(message))
|
|
|
|
|
|
|
|
.model(ChatCompletion.Model.GPT_4_VISION_PREVIEW.getName())
|
|
|
|
|
|
|
|
.build();
|
|
|
|
|
|
|
|
ChatCompletionResponse chatCompletionResponse = openAiClient.chatCompletion(chatCompletion);
|
|
|
|
|
|
|
|
chatCompletionResponse.getChoices().forEach(e -> System.out.println(e.getMessage()));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public SseEmitter pictureChatStream(String text,String imgUrl, SysUser sysUser) throws IOException {
|
|
|
|
|
|
|
|
Content textContent = Content.builder().text(text).type(Content.Type.TEXT.getName()).build();
|
|
|
|
|
|
|
|
ImageUrl imageUrl = ImageUrl.builder().url(imgUrl).build();
|
|
|
|
|
|
|
|
Content imageContent = Content.builder().imageUrl(imageUrl).type(Content.Type.IMAGE_URL.getName()).build();
|
|
|
|
|
|
|
|
SseEmitter sseEmitter = new SseEmitter(0l);
|
|
|
|
|
|
|
|
sseEmitter.send(SseEmitter.event().id("1").name("连接成功!!!!").data(LocalDateTime.now()).reconnectTime(3000));
|
|
|
|
|
|
|
|
sseEmitter.onCompletion(() -> {
|
|
|
|
|
|
|
|
log.info(LocalDateTime.now() + "on completion");
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
sseEmitter.onTimeout(() -> log.info(LocalDateTime.now() + ", on timeout#" + sseEmitter.getTimeout()));
|
|
|
|
|
|
|
|
sseEmitter.onError(
|
|
|
|
|
|
|
|
throwable -> {
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
log.info(LocalDateTime.now() + ", uid#" + "765431" + ", on error#" + throwable.toString());
|
|
|
|
|
|
|
|
sseEmitter.send(SseEmitter.event().id("765431").name("发生异常!").data(throwable.getMessage()).reconnectTime(3000));
|
|
|
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
OpenAISSEEventSourceListener openAIEventSourceListener = new OpenAISSEEventSourceListener(sseEmitter);
|
|
|
|
|
|
|
|
List<Content> contentList = new ArrayList<>();
|
|
|
|
|
|
|
|
contentList.add(textContent);
|
|
|
|
|
|
|
|
contentList.add(imageContent);
|
|
|
|
|
|
|
|
MessagePicture message = MessagePicture.builder().role(Message.Role.USER).content(contentList).build();
|
|
|
|
|
|
|
|
ChatCompletionWithPicture chatCompletion = ChatCompletionWithPicture
|
|
|
|
|
|
|
|
.builder()
|
|
|
|
|
|
|
|
.messages(Collections.singletonList(message))
|
|
|
|
|
|
|
|
.model(ChatCompletion.Model.GPT_4_VISION_PREVIEW.getName())
|
|
|
|
|
|
|
|
.build();
|
|
|
|
|
|
|
|
openAiStreamClient.streamChatCompletion(chatCompletion, openAIEventSourceListener);
|
|
|
|
|
|
|
|
return sseEmitter;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public String generateImage(String content) {
|
|
|
|
|
|
|
|
Image image = Image.builder()
|
|
|
|
|
|
|
|
.responseFormat(com.unfbx.chatgpt.entity.images.ResponseFormat.URL.getName())
|
|
|
|
|
|
|
|
.model(Image.Model.DALL_E_3.getName())
|
|
|
|
|
|
|
|
.prompt(content)
|
|
|
|
|
|
|
|
.n(1)
|
|
|
|
|
|
|
|
.quality(Image.Quality.HD.getName())
|
|
|
|
|
|
|
|
.size(SizeEnum.size_1024_1792.getName())
|
|
|
|
|
|
|
|
.style(Image.Style.NATURAL.getName())
|
|
|
|
|
|
|
|
.build();
|
|
|
|
|
|
|
|
ImageResponse imageResponse = openAiClient.genImages(image);
|
|
|
|
|
|
|
|
return imageResponse.getData().get(0).getUrl();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public SseEmitter getStreaming(ChatCompletion chatCompletion) {
|
|
|
|
|
|
|
|
Flux<ChatCompletionResponse> streamingRes = getStreamingRes(chatCompletion);
|
|
|
|
|
|
|
|
SseEmitter sseEmitter = new SseEmitter(0l);
|
|
|
|
|
|
|
|
Flux.from(streamingRes)
|
|
|
|
|
|
|
|
.doOnNext(sseEvent -> {
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
sseEmitter.send(SseEmitter.event()
|
|
|
|
|
|
|
|
.id(sseEvent.getId())
|
|
|
|
|
|
|
|
.data(sseEvent)
|
|
|
|
|
|
|
|
.reconnectTime(3000));
|
|
|
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
.doOnError(error -> {
|
|
|
|
|
|
|
|
log.error("连接断开: {}", error.getMessage());
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
.doOnComplete(() -> {
|
|
|
|
|
|
|
|
log.info("完成");
|
|
|
|
|
|
|
|
}).subscribe();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return sseEmitter;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|