mirror of
https://gitcode.com/ageerle/ruoyi-ai.git
synced 2026-04-08 09:17:33 +00:00
init v1.0.0
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
package com.xmzs.system.cofing;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* gpt配置
|
||||
*
|
||||
* @author ashinnotfound
|
||||
* @date 2023/03/04
|
||||
*/
|
||||
@Data
|
||||
@Component
|
||||
@ConfigurationProperties("gpt")
|
||||
public class GptConfig {
|
||||
private String baseUrl;
|
||||
private String model;
|
||||
private Integer maxToken;
|
||||
private Double temperature;
|
||||
private List<String> basicPrompt;
|
||||
private List<String> apiKey;
|
||||
private Long ofSeconds;
|
||||
private String imageQuality;
|
||||
private String imageStyle;
|
||||
private String audioModel;
|
||||
private String audioVoice;
|
||||
private Double audioSpeed;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.xmzs.system.cofing;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 关键字配置
|
||||
*
|
||||
* @author ashinnotfound
|
||||
* @date 2023/08/10
|
||||
*/
|
||||
@Data
|
||||
@Component
|
||||
@ConfigurationProperties("keyword")
|
||||
public class KeywordConfig {
|
||||
private String reset;
|
||||
private String image;
|
||||
private String audio;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.xmzs.system.cofing;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* qq配置
|
||||
*
|
||||
* @author ashinnotfound
|
||||
* @date 2023/03/04
|
||||
*/
|
||||
@Data
|
||||
@Component
|
||||
@ConfigurationProperties("qq")
|
||||
public class QqConfig {
|
||||
private Boolean enable;
|
||||
private Long account;
|
||||
private Boolean acceptNewFriend;
|
||||
private Boolean acceptNewGroup;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.xmzs.system.cofing;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 微信配置
|
||||
*
|
||||
* @author ashinnotfound
|
||||
* @date 2023/03/19
|
||||
*/
|
||||
@Data
|
||||
@Component
|
||||
@ConfigurationProperties("wechat")
|
||||
public class WechatConfig {
|
||||
private Boolean enable;
|
||||
private String qrPath;
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package com.xmzs.system.handler;
|
||||
|
||||
import com.xmzs.common.chat.domain.request.ChatRequest;
|
||||
import com.xmzs.common.chat.domain.request.Dall3Request;
|
||||
import com.xmzs.common.chat.entity.chat.ChatCompletion;
|
||||
import com.xmzs.common.chat.entity.images.Item;
|
||||
import com.xmzs.common.wechat.api.MessageTools;
|
||||
import com.xmzs.common.wechat.beans.BaseMsg;
|
||||
import com.xmzs.common.wechat.core.Core;
|
||||
import com.xmzs.common.wechat.face.IMsgHandlerFace;
|
||||
import com.xmzs.system.cofing.KeywordConfig;
|
||||
import com.xmzs.system.service.ISseService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 微信消息处理程序
|
||||
*
|
||||
* @author ashinnotfound
|
||||
* @date 2023/03/19
|
||||
*/
|
||||
public class WechatMessageHandler implements IMsgHandlerFace {
|
||||
private final ISseService sseService;
|
||||
private final KeywordConfig keywordConfig;
|
||||
|
||||
public WechatMessageHandler(ISseService sseService, KeywordConfig keywordConfig) {
|
||||
this.sseService = sseService;
|
||||
this.keywordConfig = keywordConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String textMsgHandle(BaseMsg baseMsg) {
|
||||
//如果是在群聊
|
||||
if (baseMsg.isGroupMsg()){
|
||||
//存在@机器人的消息就向ChatGPT提问
|
||||
if (baseMsg.getText().contains("@"+ Core.getInstance().getNickName())){
|
||||
//去除@再提问
|
||||
String prompt = baseMsg.getText().replace("@"+ Core.getInstance().getNickName() + " ", "").trim();
|
||||
return textResponse(baseMsg.getFromUserName(), prompt);
|
||||
}
|
||||
}else {
|
||||
ChatRequest chatBO = new ChatRequest();
|
||||
chatBO.setPrompt(baseMsg.getText());
|
||||
chatBO.setModel(ChatCompletion.Model.GPT_3_5_TURBO.getName());
|
||||
return sseService.chat(chatBO);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private String textResponse(String userName, String content) {
|
||||
if (keywordConfig.getReset().equals(content)){
|
||||
return "重置会话成功";
|
||||
}else {
|
||||
ChatRequest chatBO = new ChatRequest();
|
||||
chatBO.setPrompt(content);
|
||||
chatBO.setUserId(userName);
|
||||
if (content.startsWith(keywordConfig.getImage())) {
|
||||
Dall3Request dall3Request = new Dall3Request();
|
||||
dall3Request.setPrompt(content.replaceFirst(keywordConfig.getImage() + " ", ""));
|
||||
List<Item> items = sseService.dall3(dall3Request);
|
||||
MessageTools.sendPicMsgByUserId(userName, items.get(0).getUrl());
|
||||
} else {
|
||||
chatBO.setPrompt(content);
|
||||
}
|
||||
}
|
||||
return "这个问题我还没学会呢";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String picMsgHandle(BaseMsg baseMsg) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String voiceMsgHandle(BaseMsg baseMsg) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String viedoMsgHandle(BaseMsg baseMsg) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String nameCardMsgHandle(BaseMsg baseMsg) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sysMsgHandle(BaseMsg baseMsg) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String verifyAddFriendMsgHandle(BaseMsg baseMsg) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String mediaMsgHandle(BaseMsg baseMsg) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -3,18 +3,14 @@ package com.xmzs.system.listener;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.xmzs.common.chat.config.LocalCache;
|
||||
import com.xmzs.common.chat.entity.chat.BaseChatCompletion;
|
||||
import com.xmzs.common.chat.constant.OpenAIConst;
|
||||
import com.xmzs.common.chat.entity.chat.ChatCompletionResponse;
|
||||
import com.xmzs.common.chat.utils.TikTokensUtil;
|
||||
import com.xmzs.common.core.domain.model.LoginUser;
|
||||
import com.xmzs.common.core.exception.base.BaseException;
|
||||
import com.xmzs.common.core.utils.SpringUtils;
|
||||
import com.xmzs.common.core.utils.StringUtils;
|
||||
import com.xmzs.common.satoken.utils.LoginHelper;
|
||||
import com.xmzs.system.domain.bo.ChatMessageBo;
|
||||
import com.xmzs.system.service.ChatService;
|
||||
import com.xmzs.system.service.IChatService;
|
||||
import com.xmzs.system.service.IChatMessageService;
|
||||
import com.xmzs.system.service.TextReviewService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -69,16 +65,28 @@ public class SSEEventSourceListener extends EventSourceListener {
|
||||
//成功响应
|
||||
emitter.complete();
|
||||
if(StringUtils.isNotEmpty(modelName)){
|
||||
ChatService chatService = SpringUtils.context().getBean(ChatService.class);
|
||||
// 扣除余额
|
||||
int tokens = TikTokensUtil.tokens(modelName,stringBuffer.toString());
|
||||
IChatService IChatService = SpringUtils.context().getBean(IChatService.class);
|
||||
IChatMessageService chatMessageService = SpringUtils.context().getBean(IChatMessageService.class);
|
||||
ChatMessageBo chatMessageBo = new ChatMessageBo();
|
||||
chatMessageBo.setModelName(modelName);
|
||||
chatMessageBo.setContent(stringBuffer.toString());
|
||||
chatMessageBo.setTotalTokens(tokens);
|
||||
Long userId = (Long)LocalCache.CACHE.get("userId");
|
||||
chatMessageBo.setUserId(userId);
|
||||
chatService.deductToken(chatMessageBo);
|
||||
if("gpt-4-all".equals(modelName)
|
||||
|| modelName.startsWith("gpt-4-gizmo")
|
||||
|| modelName.startsWith("net")){
|
||||
// 扣除余额
|
||||
IChatService.deductUserBalance(userId, OpenAIConst.GPT4_ALL_COST);
|
||||
chatMessageBo.setDeductCost(OpenAIConst.GPT4_ALL_COST);
|
||||
chatMessageBo.setTotalTokens(0);
|
||||
// 保存消息记录
|
||||
chatMessageService.insertByBo(chatMessageBo);
|
||||
}else {
|
||||
// 扣除余额
|
||||
int tokens = TikTokensUtil.tokens(modelName,stringBuffer.toString());
|
||||
chatMessageBo.setTotalTokens(tokens);
|
||||
IChatService.deductToken(chatMessageBo);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.xmzs.system.service;
|
||||
|
||||
import com.xmzs.system.domain.bo.ChatMessageBo;
|
||||
|
||||
/**
|
||||
* @author hncboy
|
||||
* @date 2023/3/22 19:41
|
||||
* 聊天相关业务接口
|
||||
*/
|
||||
public interface IChatService {
|
||||
|
||||
|
||||
/**
|
||||
* 根据消耗的tokens扣除余额
|
||||
*
|
||||
* @param chatMessageBo
|
||||
* @return 结果
|
||||
*/
|
||||
|
||||
void deductToken(ChatMessageBo chatMessageBo);
|
||||
|
||||
/**
|
||||
* 扣除用户的余额
|
||||
*
|
||||
*/
|
||||
void deductUserBalance(Long userId, Double numberCost);
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.xmzs.system.service;
|
||||
|
||||
|
||||
import com.xmzs.common.chat.domain.request.ChatRequest;
|
||||
import com.xmzs.common.chat.domain.request.Dall3Request;
|
||||
import com.xmzs.common.chat.domain.request.MjTaskRequest;
|
||||
import com.xmzs.common.chat.entity.Tts.TextToSpeech;
|
||||
import com.xmzs.common.chat.entity.images.Item;
|
||||
import com.xmzs.common.chat.entity.whisper.WhisperResponse;
|
||||
import okhttp3.ResponseBody;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 描述:
|
||||
*
|
||||
* @author https:www.unfbx.com
|
||||
* @date 2023-04-08
|
||||
*/
|
||||
public interface ISseService {
|
||||
|
||||
/**
|
||||
* 客户端发送消息到服务端
|
||||
* @param chatRequest
|
||||
*/
|
||||
SseEmitter sseChat(ChatRequest chatRequest);
|
||||
|
||||
/**
|
||||
* 语音转文字
|
||||
* @param file
|
||||
*/
|
||||
WhisperResponse speechToTextTranscriptionsV2(MultipartFile file);
|
||||
|
||||
/**
|
||||
* 文字转语音
|
||||
*/
|
||||
ResponseEntity<Resource> textToSpeed(TextToSpeech textToSpeech);
|
||||
|
||||
/**
|
||||
* 客户端发送消息到服务端
|
||||
* @param chatRequest
|
||||
*/
|
||||
String chat(ChatRequest chatRequest);
|
||||
|
||||
/**
|
||||
* 绘画接口
|
||||
* @param request
|
||||
*/
|
||||
List<Item> dall3(Dall3Request request);
|
||||
|
||||
|
||||
void mjTask(MjTaskRequest mjTaskRequest);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package com.xmzs.system.service.impl;
|
||||
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.xmzs.common.chat.entity.chat.ChatCompletion;
|
||||
import com.xmzs.common.core.exception.ServiceException;
|
||||
import com.xmzs.system.domain.ChatToken;
|
||||
import com.xmzs.system.domain.SysUser;
|
||||
import com.xmzs.system.domain.bo.ChatMessageBo;
|
||||
import com.xmzs.system.mapper.SysUserMapper;
|
||||
import com.xmzs.system.service.IChatService;
|
||||
import com.xmzs.system.service.IChatMessageService;
|
||||
import com.xmzs.system.service.IChatTokenService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @author hncboy
|
||||
* @date 2023/3/22 19:41
|
||||
* 聊天相关业务实现类
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class IChatServiceImpl implements IChatService {
|
||||
|
||||
private final SysUserMapper sysUserMapper;
|
||||
|
||||
|
||||
private final IChatMessageService chatMessageService;
|
||||
|
||||
private final IChatTokenService chatTokenService;
|
||||
|
||||
|
||||
/**
|
||||
* 根据消耗的tokens扣除余额
|
||||
*
|
||||
* @param chatMessageBo
|
||||
*
|
||||
*/
|
||||
public void deductToken(ChatMessageBo chatMessageBo) {
|
||||
// 计算总token数
|
||||
ChatToken chatToken = chatTokenService.queryByUserId(chatMessageBo.getUserId(), chatMessageBo.getModelName());
|
||||
if(chatToken == null){
|
||||
chatToken = new ChatToken();
|
||||
chatToken.setToken(0);
|
||||
}
|
||||
int totalTokens = chatToken.getToken()+ chatMessageBo.getTotalTokens();
|
||||
// 如果总token数大于等于1000,进行费用扣除
|
||||
if (totalTokens >= 1000) {
|
||||
// 计算费用
|
||||
int token1 = totalTokens / 1000;
|
||||
int token2 = totalTokens % 1000;
|
||||
if(token2 > 0){
|
||||
// 保存剩余tokens
|
||||
chatToken.setToken(token2);
|
||||
chatTokenService.editToken(chatToken);
|
||||
}else {
|
||||
chatTokenService.resetToken(chatMessageBo.getUserId(), chatMessageBo.getModelName());
|
||||
}
|
||||
chatMessageBo.setDeductCost(token1 * ChatCompletion.getModelCost(chatMessageBo.getModelName()));
|
||||
// 扣除用户余额
|
||||
deductUserBalance(chatMessageBo.getUserId(), chatMessageBo.getDeductCost());
|
||||
} else {
|
||||
chatMessageBo.setDeductCost(0d);
|
||||
chatMessageBo.setRemark("不满1kToken,计入下一次!");
|
||||
chatToken.setToken(totalTokens);
|
||||
chatToken.setModelName(chatMessageBo.getModelName());
|
||||
chatToken.setUserId(chatMessageBo.getUserId());
|
||||
chatTokenService.editToken(chatToken);
|
||||
}
|
||||
// 保存消息记录
|
||||
chatMessageService.insertByBo(chatMessageBo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从用户余额中扣除费用
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param numberCost 要扣除的费用
|
||||
*/
|
||||
@Override
|
||||
public void deductUserBalance(Long userId, Double numberCost) {
|
||||
SysUser sysUser = sysUserMapper.selectById(userId);
|
||||
if (sysUser == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Double userBalance = sysUser.getUserBalance();
|
||||
if (userBalance < numberCost) {
|
||||
throw new ServiceException("余额不足,请联系管理员充值!");
|
||||
}
|
||||
|
||||
sysUserMapper.update(null,
|
||||
new LambdaUpdateWrapper<SysUser>()
|
||||
.set(SysUser::getUserBalance, Math.max(userBalance - numberCost, 0))
|
||||
.eq(SysUser::getUserId, userId));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,294 @@
|
||||
package com.xmzs.system.service.impl;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import com.xmzs.common.chat.config.LocalCache;
|
||||
import com.xmzs.common.chat.constant.OpenAIConst;
|
||||
import com.xmzs.common.chat.domain.request.ChatRequest;
|
||||
import com.xmzs.common.chat.domain.request.Dall3Request;
|
||||
import com.xmzs.common.chat.domain.request.MjTaskRequest;
|
||||
import com.xmzs.common.chat.entity.Tts.TextToSpeech;
|
||||
import com.xmzs.common.chat.entity.chat.*;
|
||||
import com.xmzs.common.chat.entity.images.Image;
|
||||
import com.xmzs.common.chat.entity.images.ImageResponse;
|
||||
import com.xmzs.common.chat.entity.images.Item;
|
||||
import com.xmzs.common.chat.entity.whisper.WhisperResponse;
|
||||
import com.xmzs.common.chat.openai.OpenAiStreamClient;
|
||||
import com.xmzs.common.chat.utils.TikTokensUtil;
|
||||
import com.xmzs.common.core.domain.model.LoginUser;
|
||||
import com.xmzs.common.core.exception.ServiceException;
|
||||
import com.xmzs.common.core.exception.base.BaseException;
|
||||
import com.xmzs.common.core.utils.StringUtils;
|
||||
import com.xmzs.common.satoken.utils.LoginHelper;
|
||||
import com.xmzs.system.domain.SysUser;
|
||||
import com.xmzs.system.domain.bo.ChatMessageBo;
|
||||
import com.xmzs.system.listener.SSEEventSourceListener;
|
||||
import com.xmzs.system.mapper.SysUserMapper;
|
||||
import com.xmzs.system.service.IChatService;
|
||||
import com.xmzs.system.service.IChatMessageService;
|
||||
|
||||
import com.xmzs.system.service.ISseService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import okhttp3.ResponseBody;
|
||||
import org.springframework.core.io.InputStreamResource;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
import org.springframework.http.MediaType;
|
||||
import java.io.*;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import org.springframework.core.io.InputStreamResource;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import okhttp3.ResponseBody;
|
||||
|
||||
/**
|
||||
* 描述:
|
||||
*
|
||||
* @author https:www.unfbx.com
|
||||
* @date 2023-04-08
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class ISseServiceImpl implements ISseService {
|
||||
|
||||
private final OpenAiStreamClient openAiStreamClient;
|
||||
|
||||
|
||||
private final IChatService IChatService;
|
||||
|
||||
private final SysUserMapper sysUserMapper;
|
||||
|
||||
private final IChatMessageService chatMessageService;
|
||||
|
||||
@Override
|
||||
public SseEmitter sseChat(ChatRequest chatRequest) {
|
||||
LocalCache.CACHE.put("userId",getUserId());
|
||||
SseEmitter sseEmitter = new SseEmitter(0L);
|
||||
SSEEventSourceListener openAIEventSourceListener = new SSEEventSourceListener(sseEmitter);
|
||||
checkUserGrade(sseEmitter, chatRequest.getModel());
|
||||
// 获取对话消息列表
|
||||
List<Message> msgList = chatRequest.getMessages();
|
||||
// 图文识别上下文信息
|
||||
List<Content> contentList = chatRequest.getContent();
|
||||
// 消息记录
|
||||
Message message = msgList.get(msgList.size() - 1);
|
||||
ChatMessageBo chatMessageBo = new ChatMessageBo();
|
||||
chatMessageBo.setUserId(getUserId());
|
||||
chatMessageBo.setModelName(chatRequest.getModel());
|
||||
chatMessageBo.setContent(message.getContent());
|
||||
|
||||
// 图文识别模型
|
||||
if (ChatCompletion.Model.GPT_4_VISION_PREVIEW.getName().equals(chatRequest.getModel())) {
|
||||
MessagePicture messagePicture = MessagePicture.builder().role(Message.Role.USER.getName()).content(contentList).build();
|
||||
ChatCompletionWithPicture chatCompletion = ChatCompletionWithPicture
|
||||
.builder()
|
||||
.messages(Collections.singletonList(messagePicture))
|
||||
.model(chatRequest.getModel())
|
||||
.temperature(chatRequest.getTemperature())
|
||||
.topP(chatRequest.getTop_p())
|
||||
.stream(true)
|
||||
.build();
|
||||
openAiStreamClient.streamChatCompletion(chatCompletion, openAIEventSourceListener);
|
||||
// 扣除图文对话费用
|
||||
IChatService.deductUserBalance(getUserId(),OpenAIConst.GPT4_COST);
|
||||
String text = contentList.get(contentList.size() - 1).getText();
|
||||
// 保存消息记录
|
||||
chatMessageBo.setContent(text);
|
||||
chatMessageBo.setDeductCost(OpenAIConst.GPT4_COST);
|
||||
chatMessageBo.setTotalTokens(0);
|
||||
chatMessageService.insertByBo(chatMessageBo);
|
||||
} else {
|
||||
ChatCompletion completion = ChatCompletion
|
||||
.builder()
|
||||
.messages(msgList)
|
||||
.model(chatRequest.getModel())
|
||||
.temperature(chatRequest.getTemperature())
|
||||
.topP(chatRequest.getTop_p())
|
||||
.stream(true)
|
||||
.build();
|
||||
openAiStreamClient.streamChatCompletion(completion, openAIEventSourceListener);
|
||||
|
||||
if("gpt-4-all".equals(chatRequest.getModel())
|
||||
|| chatRequest.getModel().startsWith("gpt-4-gizmo")
|
||||
|| chatRequest.getModel().startsWith("net")
|
||||
){
|
||||
chatMessageBo.setDeductCost(0.0);
|
||||
// 保存消息记录
|
||||
chatMessageService.insertByBo(chatMessageBo);
|
||||
}else {
|
||||
// 扣除余额
|
||||
int tokens = TikTokensUtil.tokens(chatRequest.getModel(), msgList);
|
||||
chatMessageBo.setTotalTokens(tokens);
|
||||
IChatService.deductToken(chatMessageBo);
|
||||
}
|
||||
}
|
||||
return sseEmitter;
|
||||
}
|
||||
|
||||
/**
|
||||
* 文字转语音
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public ResponseEntity<Resource> textToSpeed(TextToSpeech textToSpeech) {
|
||||
ResponseBody body = openAiStreamClient.textToSpeechClone(textToSpeech);
|
||||
if (body != null) {
|
||||
// 将ResponseBody转换为InputStreamResource
|
||||
InputStreamResource resource = new InputStreamResource(body.byteStream());
|
||||
|
||||
// 创建并返回ResponseEntity
|
||||
return ResponseEntity.ok()
|
||||
.contentType(MediaType.parseMediaType("audio/mpeg")) // 假设是MP3文件
|
||||
.body(resource);
|
||||
|
||||
} else {
|
||||
// 如果ResponseBody为空,返回404状态码
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 语音转文字
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public WhisperResponse speechToTextTranscriptionsV2(MultipartFile file) {
|
||||
// 确保文件不为空
|
||||
if (file.isEmpty()) {
|
||||
throw new IllegalStateException("Cannot convert an empty MultipartFile");
|
||||
}
|
||||
// 创建一个文件对象
|
||||
File fileA = new File(System.getProperty("java.io.tmpdir") + File.separator + file.getOriginalFilename());
|
||||
try {
|
||||
// 将 MultipartFile 的内容写入文件
|
||||
file.transferTo(fileA);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to convert MultipartFile to File", e);
|
||||
}
|
||||
|
||||
return openAiStreamClient.speechToTextTranscriptions(fileA);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String chat(ChatRequest chatRequest) {
|
||||
Message message = Message.builder().role(Message.Role.USER).content(chatRequest.getPrompt()).build();
|
||||
ChatCompletion chatCompletion = ChatCompletion
|
||||
.builder()
|
||||
.messages(Collections.singletonList(message))
|
||||
.model(chatRequest.getModel())
|
||||
.build();
|
||||
ChatCompletionResponse chatCompletionResponse = openAiStreamClient.chatCompletion(chatCompletion);
|
||||
return chatCompletionResponse.getChoices().get(0).getMessage().getContent();
|
||||
}
|
||||
|
||||
/**
|
||||
* dall-e-3绘画接口
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
public List<Item> dall3(Dall3Request request) {
|
||||
checkUserGrade(null,"");
|
||||
// DALL3 绘图模型
|
||||
Image image = Image.builder()
|
||||
.responseFormat(com.xmzs.common.chat.entity.images.ResponseFormat.URL.getName())
|
||||
.model(Image.Model.DALL_E_3.getName())
|
||||
.prompt(request.getPrompt())
|
||||
.n(1)
|
||||
.quality(request.getQuality())
|
||||
.size(request.getSize())
|
||||
.style(request.getStyle())
|
||||
.build();
|
||||
ImageResponse imageResponse = openAiStreamClient.genImages(image);
|
||||
|
||||
// 扣除费用
|
||||
if(Objects.equals(request.getSize(), "1792x1024") || Objects.equals(request.getSize(), "1024x1792")){
|
||||
IChatService.deductUserBalance(getUserId(),OpenAIConst.DALL3_HD_COST);
|
||||
}else {
|
||||
IChatService.deductUserBalance(getUserId(),OpenAIConst.DALL3_COST);
|
||||
}
|
||||
// 保存消息记录
|
||||
ChatMessageBo chatMessageBo = new ChatMessageBo();
|
||||
chatMessageBo.setUserId(getUserId());
|
||||
chatMessageBo.setModelName(Image.Model.DALL_E_3.getName());
|
||||
chatMessageBo.setContent(request.getPrompt());
|
||||
chatMessageBo.setDeductCost(OpenAIConst.GPT4_COST);
|
||||
chatMessageBo.setTotalTokens(0);
|
||||
chatMessageService.insertByBo(chatMessageBo);
|
||||
return imageResponse.getData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mjTask(MjTaskRequest mjTaskRequest) {
|
||||
// 检验是否是付费用户
|
||||
checkUserGrade(null,"");
|
||||
//扣除费用
|
||||
IChatService.deductUserBalance(getUserId(),0.5);
|
||||
// 保存消息记录
|
||||
ChatMessageBo chatMessageBo = new ChatMessageBo();
|
||||
chatMessageBo.setUserId(getUserId());
|
||||
chatMessageBo.setModelName("mj");
|
||||
chatMessageBo.setContent(mjTaskRequest.getPrompt());
|
||||
chatMessageBo.setDeductCost(0.5);
|
||||
chatMessageBo.setTotalTokens(0);
|
||||
chatMessageService.insertByBo(chatMessageBo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断用户是否付费
|
||||
*/
|
||||
public void checkUserGrade(SseEmitter emitter, String model) {
|
||||
SysUser sysUser = sysUserMapper.selectById(getUserId());
|
||||
if(StringUtils.isEmpty(model)){
|
||||
if("0".equals(sysUser.getUserGrade())){
|
||||
throw new ServiceException("免费用户暂时不支持此模型,请切换gpt-3.5-turbo模型或者点击《进入市场选购您的商品》充值后使用!",500);
|
||||
}
|
||||
}
|
||||
// TODO 添加枚举
|
||||
if ("0".equals(sysUser.getUserGrade()) && !ChatCompletion.Model.GPT_3_5_TURBO.getName().equals(model)) {
|
||||
// 创建并发送一个名为 "error" 的事件,带有错误消息和状态码
|
||||
SseEmitter.SseEventBuilder event = SseEmitter.event()
|
||||
.name("error") // 客户端将监听这个事件名
|
||||
.data("免费用户暂时不支持此模型,请切换gpt-3.5-turbo模型或者点击《进入市场选购您的商品》充值后使用!");
|
||||
try {
|
||||
emitter.send(event);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
emitter.complete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户Id
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Long getUserId(){
|
||||
LoginUser loginUser = LoginHelper.getLoginUser();
|
||||
if (loginUser == null) {
|
||||
throw new BaseException("用户未登录!");
|
||||
}
|
||||
return loginUser.getUserId();
|
||||
}
|
||||
|
||||
public static double calculateMjCost(String speed) {
|
||||
return switch (speed) {
|
||||
case "mj-relax" -> 0.2; // Handles null and "mj-relax"
|
||||
case "mj-fast" -> 0.5;
|
||||
case "mj-turbo" -> 1.0;
|
||||
default -> 0.5; // Default cost if none of the above speeds match
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
//package com.xmzs.system.util;
|
||||
//
|
||||
//
|
||||
//import com.xmzs.system.cofing.GptConfig;
|
||||
//import com.xmzs.system.domain.ChatMessage;
|
||||
//import jakarta.annotation.PostConstruct;
|
||||
//import lombok.Getter;
|
||||
//import lombok.RequiredArgsConstructor;
|
||||
//import org.springframework.stereotype.Component;
|
||||
//
|
||||
//import java.util.*;
|
||||
//
|
||||
///**
|
||||
// * bot工具类
|
||||
// *
|
||||
// * @author ashinnotfound
|
||||
// * @date 2023/2/1
|
||||
// */
|
||||
//@Component
|
||||
//@RequiredArgsConstructor
|
||||
//public class BotUtil {
|
||||
//
|
||||
// private final GptConfig gptConfig;
|
||||
//
|
||||
// private GptClient gptClient;
|
||||
//
|
||||
// private Tokenizer tokenizer;
|
||||
//
|
||||
// private final Map<String, List<ChatMessage>> PROMPT_MAP = new HashMap<>();
|
||||
// private final Map<OpenAiService, Integer> COUNT_FOR_OPEN_AI_SERVICE = new HashMap<>();
|
||||
// @Getter
|
||||
// private ChatCompletionRequest.ChatCompletionRequestBuilder completionRequestBuilder;
|
||||
// private final List<ChatMessage> BASIC_PROMPT_LIST = new ArrayList<>();
|
||||
//
|
||||
// @PostConstruct
|
||||
// public void init() {
|
||||
// completionRequestBuilder = ChatCompletionRequest.builder().model(gptConfig.getModel()).temperature(gptConfig.getTemperature()).maxTokens(gptConfig.getMaxToken());
|
||||
// for (OpenAiService openAiService : gptClient.getOpenAiServiceList()) {
|
||||
// COUNT_FOR_OPEN_AI_SERVICE.put(openAiService, 0);
|
||||
// }
|
||||
// for (String prompt : gptConfig.getBasicPrompt()){
|
||||
// BASIC_PROMPT_LIST.add(new ChatMessage("system", prompt));
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public OpenAiService getOpenAiService() {
|
||||
// //获取使用次数最小的openAiService 否则获取map中的第一个
|
||||
// Optional<OpenAiService> openAiServiceToUse = COUNT_FOR_OPEN_AI_SERVICE.entrySet().stream()
|
||||
// .min(Map.Entry.comparingByValue())
|
||||
// .map(Map.Entry::getKey);
|
||||
// if (openAiServiceToUse.isPresent()) {
|
||||
// COUNT_FOR_OPEN_AI_SERVICE.put(openAiServiceToUse.get(), COUNT_FOR_OPEN_AI_SERVICE.get(openAiServiceToUse.get()) + 1);
|
||||
// return openAiServiceToUse.get();
|
||||
// } else {
|
||||
// COUNT_FOR_OPEN_AI_SERVICE.put(COUNT_FOR_OPEN_AI_SERVICE.keySet().iterator().next(), COUNT_FOR_OPEN_AI_SERVICE.get(COUNT_FOR_OPEN_AI_SERVICE.keySet().iterator().next()) + 1);
|
||||
// return COUNT_FOR_OPEN_AI_SERVICE.keySet().iterator().next();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public List<ChatMessage> buildPrompt(String sessionId, String newPrompt) {
|
||||
// if (!PROMPT_MAP.containsKey(sessionId)) {
|
||||
// if (!BASIC_PROMPT_LIST.isEmpty()){
|
||||
// List<ChatMessage> promptList = new ArrayList<>(BASIC_PROMPT_LIST);
|
||||
// PROMPT_MAP.put(sessionId, promptList);
|
||||
// }
|
||||
// }
|
||||
// List<ChatMessage> promptList = PROMPT_MAP.getOrDefault(sessionId, new ArrayList<>());
|
||||
// promptList.add(new ChatMessage("user", newPrompt));
|
||||
// if (tokenizer.countMessageTokens(gptConfig.getModel(), promptList) > gptConfig.getMaxToken()){
|
||||
// List<ChatMessage> tempChatMessage = deleteFirstPrompt(sessionId);
|
||||
// if (tempChatMessage != null){
|
||||
// return buildPrompt(sessionId, newPrompt);
|
||||
// }
|
||||
// return null;
|
||||
// }
|
||||
// return promptList;
|
||||
// }
|
||||
//
|
||||
// public boolean isPromptEmpty(String sessionId){
|
||||
// if (!PROMPT_MAP.containsKey(sessionId)){
|
||||
// return true;
|
||||
// }
|
||||
// return PROMPT_MAP.get(sessionId).size() == BASIC_PROMPT_LIST.size();
|
||||
// }
|
||||
// public List<ChatMessage> deleteFirstPrompt(String sessionId) {
|
||||
// if (!isPromptEmpty(sessionId)){
|
||||
// int index = BASIC_PROMPT_LIST.size();
|
||||
// List<ChatMessage> promptList = PROMPT_MAP.get(sessionId);
|
||||
// //问
|
||||
// promptList.remove(index);
|
||||
// //答
|
||||
// if (index < promptList.size()){
|
||||
// promptList.remove(index);
|
||||
// return promptList;
|
||||
// }else {
|
||||
// // 已经是初始聊天记录
|
||||
// return null;
|
||||
// }
|
||||
// }
|
||||
// // 已经是初始聊天记录
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// public void resetPrompt(String sessionId) {
|
||||
// PROMPT_MAP.remove(sessionId);
|
||||
// }
|
||||
//}
|
||||
Reference in New Issue
Block a user