99AI/dist/modules/chat/chat.service.js
2024-05-18 20:51:28 +08:00

1300 lines
64 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ChatService = void 0;
const common_1 = require("@nestjs/common");
const nestjs_config_1 = require("nestjs-config");
const upload_service_1 = require("../upload/upload.service");
const user_service_1 = require("../user/user.service");
const errorMessage_constant_1 = require("../../common/constants/errorMessage.constant");
const utils_1 = require("../../common/utils");
const typeorm_1 = require("@nestjs/typeorm");
const axios_1 = require("axios");
const typeorm_2 = require("typeorm");
const uuid = require("uuid");
const autoreply_service_1 = require("../autoreply/autoreply.service");
const badwords_service_1 = require("../badwords/badwords.service");
const chatLog_service_1 = require("../chatLog/chatLog.service");
const config_entity_1 = require("../globalConfig/config.entity");
const globalConfig_service_1 = require("../globalConfig/globalConfig.service");
const userBalance_service_1 = require("../userBalance/userBalance.service");
const apiDataService_service_1 = require("./apiDataService.service");
const tiktoken_1 = require("@dqbd/tiktoken");
const app_entity_1 = require("../app/app.entity");
const chatGroup_service_1 = require("../chatGroup/chatGroup.service");
const models_service_1 = require("../models/models.service");
const chatBox_entity_1 = require("./chatBox.entity");
const chatBoxType_entity_1 = require("./chatBoxType.entity");
const chatPre_entity_1 = require("./chatPre.entity");
const chatPreType_entity_1 = require("./chatPreType.entity");
const store_1 = require("./store");
let ChatService = class ChatService {
constructor(configEntity, chatBoxTypeEntity, chatBoxEntity, appEntity, chatPreTypeEntity, chatPreEntity, apiDataService, chatLogService, configService, userBalanceService, userService, uploadService, badwordsService, autoreplyService, globalConfigService, chatGroupService, modelsService) {
this.configEntity = configEntity;
this.chatBoxTypeEntity = chatBoxTypeEntity;
this.chatBoxEntity = chatBoxEntity;
this.appEntity = appEntity;
this.chatPreTypeEntity = chatPreTypeEntity;
this.chatPreEntity = chatPreEntity;
this.apiDataService = apiDataService;
this.chatLogService = chatLogService;
this.configService = configService;
this.userBalanceService = userBalanceService;
this.userService = userService;
this.uploadService = uploadService;
this.badwordsService = badwordsService;
this.autoreplyService = autoreplyService;
this.globalConfigService = globalConfigService;
this.chatGroupService = chatGroupService;
this.modelsService = modelsService;
this.nineStore = null;
}
async onModuleInit() {
let KeyvRedis = await (0, utils_1.importDynamic)('@keyv/redis');
let Keyv = await (0, utils_1.importDynamic)('keyv');
KeyvRedis = (KeyvRedis === null || KeyvRedis === void 0 ? void 0 : KeyvRedis.default) ? KeyvRedis.default : KeyvRedis;
Keyv = (Keyv === null || Keyv === void 0 ? void 0 : Keyv.default) ? Keyv.default : Keyv;
const port = +process.env.REDIS_PORT;
const host = process.env.REDIS_HOST;
const password = process.env.REDIS_PASSWORD;
const username = process.env.REDIS_USER;
const redisUrl = `redis://${username || ''}:${password || ''}@${host}:${port}`;
const store = new KeyvRedis(redisUrl);
const messageStore = new Keyv({ store, namespace: 'ai-web' });
this.nineStore = new store_1.NineStore({ store: messageStore, namespace: 'chat' });
}
async ttsProcess(body, req, res) {
const { id } = req.user;
const { chatId, prompt } = body;
const detailKeyInfo = await this.modelsService.getCurrentModelKeyInfo('tts-1');
const { openaiTimeout, openaiBaseUrl, openaiBaseKey, } = await this.globalConfigService.getConfigs([
'openaiTimeout',
'openaiBaseUrl',
'openaiBaseKey',
]);
const { key, proxyUrl, deduct, deductType, timeout } = detailKeyInfo;
let useKey = key || openaiBaseKey;
let useUrl = proxyUrl || openaiBaseUrl;
let useTimeout = (timeout || openaiTimeout) * 1000;
await this.userBalanceService.validateBalance(req, deductType, deduct);
console.log('开始 TTS 请求:', prompt, 'TTSService');
const options = {
method: 'POST',
url: `${useUrl}/v1/audio/speech`,
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${useKey}`,
},
responseType: 'arraybuffer',
timeout: useTimeout,
data: {
model: 'tts-1',
input: prompt,
voice: "onyx"
},
};
let ttsUrl;
try {
const response = await (0, axios_1.default)(options);
console.log('TTS 请求获取成功', 'TTSService');
const buffer = Buffer.from(response.data);
try {
const filename = uuid.v4().slice(0, 10) + '.mp3';
common_1.Logger.log(`------> 开始上传语音!!!`, 'TTSService');
ttsUrl = await this.uploadService.uploadFile({ filename, buffer });
common_1.Logger.log(`文件上传成功URL: ${ttsUrl}`, 'TTSService');
}
catch (error) {
common_1.Logger.error(`上传图片过程中出现错误: ${error}`, 'TTSService');
}
await this.chatLogService.updateChatLog(chatId, {
ttsUrl: ttsUrl
});
await this.userBalanceService.deductFromBalance(req.user.id, deductType, deduct);
res.status(200).send({ ttsUrl });
}
catch (error) {
console.error('TTS request failed:', error);
throw new Error('Failed to process TTS request');
}
}
async chatProcess(body, req, res) {
var _a, _b, _c;
const { options = {}, appId = null, specialModel, prompt, fileInfo, modelType, extraParam, model, drawId, customId, action } = body;
let appInfo;
if (specialModel) {
appInfo = await this.appEntity.findOne({ where: { des: specialModel, isSystemReserved: true } });
}
else if (appId) {
appInfo = await this.appEntity.findOne({ where: { id: appId, status: (0, typeorm_2.In)([1, 3, 4, 5]) } });
if (!appInfo) {
throw new common_1.HttpException('你当前使用的应用已被下架、请删除当前对话开启新的对话吧!', common_1.HttpStatus.BAD_REQUEST);
}
}
const { groupId, usingNetwork, fileParsing, usingMindMap } = options;
const abortController = req.abortController;
const { openaiTimeout, openaiBaseUrl, openaiBaseKey, systemPreMessage, isMjTranslate, mjTranslatePrompt, isDalleChat, } = await this.globalConfigService.getConfigs([
'openaiTimeout',
'openaiBaseUrl',
'openaiBaseKey',
'systemPreMessage',
'isMjTranslate',
'mjTranslatePrompt',
'isDalleChat',
]);
await this.userService.checkUserStatus(req.user);
res && res.setHeader('Content-type', 'application/octet-stream; charset=utf-8');
await this.badwordsService.checkBadWords(prompt, req.user.id);
let currentRequestModelKey = null;
let appName = '';
let setSystemMessage = '';
res && res.status(200);
let response = null;
const curIp = (0, utils_1.getClientIp)(req);
let isStop = true;
let usePrompt;
let isSuccess = false;
if (appInfo) {
const { isGPTs, gizmoID, name, isFixedModel, appModel } = appInfo;
appName = name;
if (isGPTs) {
currentRequestModelKey = await this.modelsService.getCurrentModelKeyInfo('gpts');
await this.chatLogService.checkModelLimits(req.user, 'gpts');
currentRequestModelKey.model = `gpt-4-gizmo-${gizmoID}`;
}
else if (!isGPTs && isFixedModel && appModel) {
appInfo.preset && (setSystemMessage = appInfo.preset);
currentRequestModelKey = await this.modelsService.getCurrentModelKeyInfo(appModel);
await this.chatLogService.checkModelLimits(req.user, appModel);
currentRequestModelKey.model = appModel;
if (fileParsing) {
setSystemMessage = `${setSystemMessage}以下是我提供给你的知识库:【${fileParsing}】,在回答问题之前,先检索知识库内有没有相关的内容,尽量使用知识库中获取到的信息来回答我的问题,以知识库中的为准。`;
}
common_1.Logger.log(`固定模型、使用应用预设: ${setSystemMessage}`);
}
else {
appInfo.preset && (setSystemMessage = appInfo.preset);
currentRequestModelKey = await this.modelsService.getCurrentModelKeyInfo(model);
await this.chatLogService.checkModelLimits(req.user, model);
if (fileParsing) {
setSystemMessage = `${setSystemMessage}以下是我提供给你的知识库:【${fileParsing}】,在回答问题之前,先检索知识库内有没有相关的内容,尽量使用知识库中获取到的信息来回答我的问题,以知识库中的为准。`;
}
common_1.Logger.log(`使用应用预设: ${setSystemMessage}`);
}
}
else {
const currentDate = new Date().toISOString().split('T')[0];
setSystemMessage = systemPreMessage + `\n Current date: ${currentDate}`;
currentRequestModelKey = await this.modelsService.getCurrentModelKeyInfo(model);
await this.chatLogService.checkModelLimits(req.user, model);
common_1.Logger.log(`使用全局预设: ${setSystemMessage}`);
}
const { deduct, isTokenBased, tokenFeeRatio, deductType, key, modelName, id: keyId, maxRounds, proxyUrl, maxModelTokens, timeout, model: useModel, isFileUpload } = currentRequestModelKey;
if (isMjTranslate === '1' && mjTranslatePrompt && model === 'midjourney') {
const translatePrompt = await this.apiDataService.chatFree(prompt, mjTranslatePrompt);
usePrompt = (isFileUpload === '1' && fileInfo) ? fileInfo + " " + translatePrompt : translatePrompt;
}
else {
usePrompt = (isFileUpload === '1' && fileInfo) ? fileInfo + " " + prompt : prompt;
}
await this.userBalanceService.validateBalance(req, deductType, deduct);
const useModeName = appName || modelName;
const proxyResUrl = proxyUrl || openaiBaseUrl || 'https://api.openai.com';
const modelKey = key || openaiBaseKey;
const modelTimeout = (timeout || openaiTimeout || 300) * 1000;
common_1.Logger.log(`超时设置: ${modelTimeout / 1000} s\n` +
`请求地址: ${proxyResUrl}\n` +
`使用的模型名称: ${useModeName}\n` +
`使用的模型: ${useModel}`);
if (!currentRequestModelKey) {
throw new common_1.HttpException('当前流程所需要的模型已被管理员下架、请联系管理员上架专属模型!', common_1.HttpStatus.BAD_REQUEST);
}
let groupInfo;
if (groupId) {
groupInfo = await this.chatGroupService.getGroupInfoFromId(groupId);
}
// if ((groupInfo === null || groupInfo === void 0 ? void 0 : groupInfo.title) === '新对话') {
// let chatTitle;
// if (modelType === 1) {
// chatTitle = await this.apiDataService.chatFree(`根据用户提问{${prompt}}给这个对话取一个名字不超过10个字`);
// }
// else {
// chatTitle = '创意 AI';
// }
// await this.chatGroupService.update({
// groupId,
// title: chatTitle,
// isSticky: false,
// config: '',
// }, req);
// common_1.Logger.log(`更新标题名称为: ${chatTitle}`);
// }
//将更新标题名称的代码改为异步函数
const updateTitleAsync = async () => {
if ((groupInfo === null || groupInfo === void 0 ? void 0 : groupInfo.title) === '新对话') {
let chatTitle;
if (modelType === 1) {
chatTitle = await this.apiDataService.chatFree(`根据用户提问{${prompt}}给这个对话取一个名字不超过10个字`);
}
else {
chatTitle = '创意 AI';
}
await this.chatGroupService.update({
groupId,
title: chatTitle,
isSticky: false,
config: '',
}, req);
common_1.Logger.log(`${groupId} 更新标题名称为: ${chatTitle}`);
}
};
// 调用异步函数
updateTitleAsync();
if (groupId) {
await this.chatGroupService.updateTime(groupId);
}
const { messagesHistory } = await this.nineStore.buildMessageFromParentMessageId(prompt, {
groupId,
systemMessage: setSystemMessage,
maxModelTokens,
maxRounds: usingNetwork || useModel.includes('suno') ? 0 : maxRounds,
fileInfo: fileInfo,
model: useModel,
isFileUpload,
}, this.chatLogService);
const userSaveLog = await this.chatLogService.saveChatLog({
appId,
curIp,
userId: req.user.id,
type: modelType,
prompt,
fileInfo: fileInfo,
answer: '',
promptTokens: 0,
completionTokens: 0,
totalTokens: 0,
model: useModel,
modelName: '我',
role: 'user',
groupId,
});
const pluginsUsed = JSON.stringify({
usingNetwork: !!usingNetwork,
usingMindMap: !!usingMindMap,
});
const assistantSaveLog = await this.chatLogService.saveChatLog({
appId,
curIp,
userId: req.user.id,
type: modelType,
prompt: prompt,
fileInfo: null,
answer: '',
progress: '0%',
promptTokens: 0,
completionTokens: 0,
totalTokens: 0,
model: useModel,
modelName: useModeName,
role: 'assistant',
groupId,
status: 2,
pluginParam: pluginsUsed,
});
const userLogId = userSaveLog.id;
const assistantLogId = assistantSaveLog.id;
const autoReplyRes = await this.autoreplyService.checkAutoReply(prompt);
if (autoReplyRes && res) {
const msg = { text: autoReplyRes };
const chars = autoReplyRes.split('');
const sendCharByChar = (index) => {
if (index < chars.length) {
const msg = { text: chars[index] };
res.write(`${JSON.stringify(msg)}\n`);
setTimeout(() => sendCharByChar(index + 1), 20);
}
else {
res.end();
}
};
sendCharByChar(0);
await this.chatLogService.updateChatLog(assistantLogId, {
answer: autoReplyRes,
});
return;
}
common_1.Logger.log('开始处理对话!');
let charge = (action !== "UPSCALE" && useModel === 'midjourney') ? deduct * 4 : deduct;
;
let isClientClosed = false;
try {
if (res) {
let lastChat;
res.on('close', async () => {
if (isSuccess) {
return;
}
isClientClosed = true;
const prompt_tokens = (await this.getTokenCount(prompt)) || 1;
const completion_tokens = (await this.getTokenCount(lastChat === null || lastChat === void 0 ? void 0 : lastChat.answer)) || 1;
const total_tokens = prompt_tokens + completion_tokens;
await this.chatLogService.updateChatLog(userLogId, {
fileInfo: fileInfo,
promptTokens: prompt_tokens,
completionTokens: completion_tokens,
totalTokens: total_tokens,
status: 4,
});
await this.chatLogService.updateChatLog(assistantLogId, {
answer: lastChat === null || lastChat === void 0 ? void 0 : lastChat.answer,
promptTokens: prompt_tokens,
completionTokens: completion_tokens,
totalTokens: total_tokens,
status: 4,
});
let charge = deduct;
if (isTokenBased === true) {
charge = Math.ceil((deduct * total_tokens) / tokenFeeRatio);
}
if (isStop) {
await this.userBalanceService.deductFromBalance(req.user.id, deductType, charge, total_tokens);
}
});
let response;
let firstChunk = true;
try {
if (useModel === 'dall-e-3' || useModel === 'midjourney' || useModel.includes('suno') || useModel.includes('stable-diffusion')) {
if (useModel === 'dall-e-3') {
let drawPrompt;
if (isDalleChat === '1') {
try {
common_1.Logger.log('已开启连续绘画模式');
const { messagesHistory } = await this.nineStore.buildMessageFromParentMessageId(`${prompt},不用包含任何礼貌性的寒暄,只需要场景的描述,可以适当联想`, {
groupId,
systemMessage: "总结我的绘画需求,然后生成绘画场景的描述",
maxModelTokens,
maxRounds,
fileInfo: fileInfo,
model: useModel,
isFileUpload,
}, this.chatLogService);
drawPrompt = await this.apiDataService.chatFree(prompt, undefined, messagesHistory);
}
catch (error) {
console.error("调用chatFree失败", error);
drawPrompt = prompt;
}
}
else {
drawPrompt = prompt;
}
response = this.apiDataService.dalleDraw(({
prompt: drawPrompt,
extraParam: extraParam,
apiKey: modelKey,
proxyUrl: proxyResUrl,
model: useModel,
timeout: modelTimeout,
modelName: useModeName,
onSuccess: async (data) => {
await this.chatLogService.updateChatLog(assistantLogId, {
fileInfo: data === null || data === void 0 ? void 0 : data.fileInfo,
answer: (data === null || data === void 0 ? void 0 : data.answer) || prompt,
progress: '100%',
status: data.status,
});
common_1.Logger.log('绘图成功! ', 'DrawService');
},
onFailure: async (data) => {
await this.chatLogService.updateChatLog(assistantLogId, {
answer: '绘图失败',
status: data.status,
});
common_1.Logger.log('绘图失败', 'DrawService');
}
}), messagesHistory);
await this.chatLogService.updateChatLog(assistantLogId, {
answer: '绘制中',
});
}
else if (useModel.includes('suno')) {
response = this.suno(messagesHistory, {
chatId: assistantLogId,
maxModelTokens,
apiKey: modelKey,
model: useModel,
modelName: useModeName,
modelType,
prompt,
fileInfo,
isFileUpload,
timeout: modelTimeout,
proxyUrl: proxyResUrl,
onGenerate: async (data) => {
await this.chatLogService.updateChatLog(assistantLogId, {
fileInfo: data === null || data === void 0 ? void 0 : data.fileInfo,
answer: (data === null || data === void 0 ? void 0 : data.answer) || prompt,
status: 2,
});
common_1.Logger.log('歌曲生成中');
},
onSuccess: async (data) => {
await this.chatLogService.updateChatLog(assistantLogId, {
fileInfo: data === null || data === void 0 ? void 0 : data.fileInfo,
answer: (data === null || data === void 0 ? void 0 : data.answer) || prompt,
progress: '100%',
status: 3,
});
common_1.Logger.log('生成歌曲成功');
},
onFailure: async (data) => {
await this.chatLogService.updateChatLog(assistantLogId, {
answer: data.errMsg,
status: 4,
});
}
});
await this.chatLogService.updateChatLog(assistantLogId, {
answer: '提交成功,歌曲生成中',
});
}
else if (useModel.includes('stable-diffusion')) {
response = this.sdxl(messagesHistory, {
chatId: assistantLogId,
maxModelTokens,
apiKey: modelKey,
model: useModel,
modelName: useModeName,
modelType,
prompt,
fileInfo,
isFileUpload,
timeout: modelTimeout,
proxyUrl: proxyResUrl,
onSuccess: async (data) => {
await this.chatLogService.updateChatLog(assistantLogId, {
fileInfo: data === null || data === void 0 ? void 0 : data.fileInfo,
answer: (data === null || data === void 0 ? void 0 : data.answer) || prompt,
progress: '100%',
status: 3,
});
},
onFailure: async (data) => {
await this.chatLogService.updateChatLog(assistantLogId, {
answer: '生成失败',
status: 4,
});
}
});
await this.chatLogService.updateChatLog(assistantLogId, {
answer: '绘制中',
});
}
else {
response = this.mjDraw({
usePrompt: usePrompt,
prompt: prompt,
apiKey: modelKey,
proxyUrl: proxyResUrl,
model: useModel,
modelName: useModeName,
drawId,
customId,
action,
timeout: modelTimeout,
assistantLogId,
});
await this.chatLogService.updateChatLog(assistantLogId, {
answer: '绘制中',
});
}
await this.modelsService.saveUseLog(keyId, 1);
await this.userBalanceService.deductFromBalance(req.user.id, deductType, charge);
const userBalance = await this.userBalanceService.queryUserBalance(req.user.id);
response.userBalance = Object.assign({}, userBalance);
response.text = '提交成功';
isStop = false;
isSuccess = true;
response.status = 2;
response.model = model;
response.modelName = modelName;
return res.write(`\n${JSON.stringify(response)}`);
}
else {
response = await this.sendMessageFromAi(messagesHistory, {
chatId: assistantLogId,
maxModelTokens,
apiKey: modelKey,
model: useModel,
modelName: useModeName,
modelType,
prompt,
fileInfo,
isFileUpload,
timeout: modelTimeout,
proxyUrl: proxyResUrl,
onProgress: (chat) => {
res.write(firstChunk ? JSON.stringify(chat) : `\n${JSON.stringify(chat)}`);
lastChat = chat;
firstChunk = false;
},
onFailure: async (data) => {
await this.chatLogService.updateChatLog(assistantLogId, {
answer: data.errMsg,
status: 4,
});
}
}, isClientClosed);
if (response.errMsg) {
isStop = false;
isSuccess = true;
common_1.Logger.error(`用户ID: ${req.user.id} 模型名称: ${useModeName} 模型: ${model} 回复出错,本次不扣除积分`, 'ChatService');
return res.write(`\n${JSON.stringify(response)}`);
}
let totalText = '';
messagesHistory.forEach(messagesHistory => {
totalText += messagesHistory.content + ' ';
});
const promptTokens = await this.getTokenCount(totalText);
const completionTokens = await this.getTokenCount(response.answer);
await this.chatLogService.updateChatLog(userLogId, {
promptTokens: promptTokens,
completionTokens: completionTokens,
totalTokens: promptTokens + completionTokens,
});
await this.chatLogService.updateChatLog(assistantLogId, {
fileInfo: response === null || response === void 0 ? void 0 : response.fileInfo,
answer: response.answer,
promptTokens: promptTokens,
completionTokens: completionTokens,
totalTokens: promptTokens + completionTokens,
status: 3
});
if (isTokenBased === true) {
charge = Math.ceil((deduct * (promptTokens + completionTokens)) / tokenFeeRatio);
}
await this.userBalanceService.deductFromBalance(req.user.id, deductType, charge, (promptTokens + completionTokens));
await this.modelsService.saveUseLog(keyId, (promptTokens + completionTokens));
common_1.Logger.log(`用户ID: ${req.user.id} 模型名称: ${useModeName} 模型: ${model} 消耗token: ${(promptTokens + completionTokens)}, 消耗积分: ${charge}`, 'ChatService');
const userBalance = await this.userBalanceService.queryUserBalance(req.user.id);
response.userBalance = Object.assign({}, userBalance);
isStop = false;
isSuccess = true;
return res.write(`\n${JSON.stringify(response)}`);
}
}
catch (error) {
common_1.Logger.error('发生错误:', error);
await this.chatLogService.updateChatLog(assistantLogId, {
status: 5,
});
response = { error: '处理请求时发生错误' };
isStop = false;
}
}
else {
response = await this.sendMessageFromAi(messagesHistory, {
chatId: assistantLogId,
maxModelTokens,
apiKey: modelKey,
model: useModel,
modelName: useModeName,
modelType,
prompt,
fileInfo,
isFileUpload,
timeout: modelTimeout,
proxyUrl: proxyResUrl,
}, isClientClosed);
await this.userBalanceService.deductFromBalance(req.user.id, deductType, charge);
return response.answer;
}
}
catch (error) {
common_1.Logger.error('chat-error <----------------------------------------->', modelKey, error);
const code = (error === null || error === void 0 ? void 0 : error.statusCode) || 400;
const status = ((_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.status) || (error === null || error === void 0 ? void 0 : error.statusCode) || 400;
common_1.Logger.error('chat-error-detail <----------------------------------------->', 'code: ', code, 'message', error === null || error === void 0 ? void 0 : error.message, 'statusText:', (_b = error === null || error === void 0 ? void 0 : error.response) === null || _b === void 0 ? void 0 : _b.statusText, 'status', (_c = error === null || error === void 0 ? void 0 : error.response) === null || _c === void 0 ? void 0 : _c.status);
if (error.status && error.status === 402) {
const errMsg = { message: `Catch Error ${error.message}`, code: 402 };
if (res) {
return res.write(JSON.stringify(errMsg));
}
else {
throw new common_1.HttpException(error.message, common_1.HttpStatus.PAYMENT_REQUIRED);
}
}
if (!status) {
if (res) {
return res.write(JSON.stringify({ message: error.message, code: 500 }));
}
else {
throw new common_1.HttpException(error.message, common_1.HttpStatus.BAD_REQUEST);
}
}
let message = errorMessage_constant_1.OpenAiErrorCodeMessage[status] ? errorMessage_constant_1.OpenAiErrorCodeMessage[status] : '服务异常、请重新试试吧!!!';
if ((error === null || error === void 0 ? void 0 : error.message.includes('The OpenAI account associated with this API key has been deactivated.')) && Number(modelType) === 1) {
await this.modelsService.lockKey(keyId, '当前模型key已被封禁、已冻结当前调用Key、尝试重新对话试试吧', -1);
message = '当前模型key已被封禁';
}
if ((error === null || error === void 0 ? void 0 : error.statusCode) === 429 && error.message.includes('billing') && Number(modelType) === 1) {
await this.modelsService.lockKey(keyId, '当前模型key余额已耗尽、已冻结当前调用Key、尝试重新对话试试吧', -3);
message = '当前模型key余额已耗尽';
}
if ((error === null || error === void 0 ? void 0 : error.statusCode) === 429 && (error === null || error === void 0 ? void 0 : error.statusText) === 'Too Many Requests') {
message = '当前模型调用过于频繁、请重新试试吧!';
}
if ((error === null || error === void 0 ? void 0 : error.statusCode) === 401 && error.message.includes('Incorrect API key provided') && Number(modelType) === 1) {
await this.modelsService.lockKey(keyId, '提供了错误的模型秘钥', -2);
message = '提供了错误的模型秘钥、已冻结当前调用Key、请重新尝试对话';
}
if ((error === null || error === void 0 ? void 0 : error.statusCode) === 404 && error.message.includes('This is not a chat model and thus not supported') && Number(modelType) === 1) {
await this.modelsService.lockKey(keyId, '当前模型不是聊天模型', -4);
message = '当前模型不是聊天模型、已冻结当前调用Key、请重新尝试对话';
}
if (code === 400) {
console.log('400 error', error, error.message);
}
const errMsg = { message: message || 'Please check the back-end console', code: code === 401 ? 400 : code || 500 };
if (res) {
return res.write(JSON.stringify(errMsg));
}
else {
throw new common_1.HttpException(errMsg.message, common_1.HttpStatus.BAD_REQUEST);
}
}
finally {
res && res.end();
}
}
async setChatBoxType(req, body) {
try {
const { name, icon, order, id, status } = body;
if (id) {
return await this.chatBoxTypeEntity.update({ id }, { name, icon, order, status });
}
else {
return await this.chatBoxTypeEntity.save({ name, icon, order, status });
}
}
catch (error) {
console.log('error: ', error);
}
}
async delChatBoxType(req, body) {
const { id } = body;
if (!id) {
throw new common_1.HttpException('非法操作!', common_1.HttpStatus.BAD_REQUEST);
}
const count = await this.chatBoxEntity.count({ where: { typeId: id } });
if (count) {
throw new common_1.HttpException('当前分类下有未处理数据不可移除!', common_1.HttpStatus.BAD_REQUEST);
}
return await this.chatBoxTypeEntity.delete({ id });
}
async queryChatBoxType() {
return await this.chatBoxTypeEntity.find({
order: { order: 'DESC' },
});
}
async setChatBox(req, body) {
const { title, prompt, appId, order, status, typeId, id, url } = body;
if (!typeId) {
throw new common_1.HttpException('缺失必要参数!', common_1.HttpStatus.BAD_REQUEST);
}
try {
const params = { title, order, status, typeId, url };
params.appId = appId || 0;
params.prompt = prompt || '';
if (id) {
return await this.chatBoxEntity.update({ id }, params);
}
else {
return await this.chatBoxEntity.save(params);
}
}
catch (error) {
console.log('error: ', error);
}
}
async delChatBox(req, body) {
const { id } = body;
if (!id) {
throw new common_1.HttpException('非法操作!', common_1.HttpStatus.BAD_REQUEST);
}
return await this.chatBoxEntity.delete({ id });
}
async queryChatBox() {
const data = await this.chatBoxEntity.find({
order: { order: 'DESC' },
});
const typeIds = [...new Set(data.map((t) => t.typeId))];
const appIds = [...new Set(data.map((t) => t.appId))];
const typeRes = await this.chatBoxTypeEntity.find({ where: { id: (0, typeorm_2.In)(typeIds) } });
const appRes = await this.appEntity.find({ where: { id: (0, typeorm_2.In)(appIds) } });
return data.map((item) => {
const { typeId, appId } = item;
item.typeInfo = typeRes.find((t) => t.id === typeId);
item.appInfo = appRes.find((t) => t.id === appId);
return item;
});
}
async queryChatBoxFrontend() {
const typeRes = await this.chatBoxTypeEntity.find({ order: { order: 'DESC' }, where: { status: true } });
const boxinfos = await this.chatBoxEntity.find({ where: { status: true } });
const appIds = [...new Set(boxinfos.map((t) => t.appId))];
const appInfos = await this.appEntity.find({ where: { id: (0, typeorm_2.In)(appIds) } });
boxinfos.forEach((item) => {
const app = appInfos.find((k) => k.id === item.appId);
item.coverImg = app === null || app === void 0 ? void 0 : app.coverImg;
return item;
});
return typeRes.map((t) => {
t.childList = boxinfos.filter((box) => box.typeId === t.id && box.status);
return t;
});
}
async setChatPreType(req, body) {
try {
const { name, icon, order, id, status } = body;
if (id) {
return await this.chatPreTypeEntity.update({ id }, { name, icon, order, status });
}
else {
return await this.chatPreTypeEntity.save({ name, icon, order, status });
}
}
catch (error) {
console.log('error: ', error);
}
}
async delChatPreType(req, body) {
const { id } = body;
if (!id) {
throw new common_1.HttpException('非法操作!', common_1.HttpStatus.BAD_REQUEST);
}
const count = await this.chatBoxEntity.count({ where: { typeId: id } });
if (count) {
throw new common_1.HttpException('当前分类下有未处理数据不可移除!', common_1.HttpStatus.BAD_REQUEST);
}
return await this.chatPreTypeEntity.delete({ id });
}
async queryChatPreType() {
return await this.chatPreTypeEntity.find({
order: { order: 'DESC' },
});
}
async setChatPre(req, body) {
const { title, prompt, appId, order, status, typeId, id, url } = body;
if (!typeId) {
throw new common_1.HttpException('缺失必要参数!', common_1.HttpStatus.BAD_REQUEST);
}
try {
const params = { title, prompt, order, status, typeId, url };
if (id) {
return await this.chatPreEntity.update({ id }, params);
}
else {
return await this.chatPreEntity.save(params);
}
}
catch (error) {
console.log('error: ', error);
}
}
async delChatPre(req, body) {
const { id } = body;
if (!id) {
throw new common_1.HttpException('非法操作!', common_1.HttpStatus.BAD_REQUEST);
}
return await this.chatPreEntity.delete({ id });
}
async queryChatPre() {
const data = await this.chatPreEntity.find({
order: { order: 'DESC' },
});
const typeIds = [...new Set(data.map((t) => t.typeId))];
const typeRes = await this.chatPreTypeEntity.find({ where: { id: (0, typeorm_2.In)(typeIds) } });
return data.map((item) => {
const { typeId, appId } = item;
item.typeInfo = typeRes.find((t) => t.id === typeId);
return item;
});
}
async queryChatPreList() {
const typeRes = await this.chatPreTypeEntity.find({ order: { order: 'DESC' }, where: { status: true } });
const chatPreData = await this.chatPreEntity.find({ where: { status: true } });
return typeRes.map((t) => {
t.childList = chatPreData.filter((box) => box.typeId === t.id && box.status);
return t;
});
}
async sdxl(messagesHistory, inputs) {
const { onGenerate, onSuccess, onFailure, apiKey, model, proxyUrl, modelName, timeout, chatId, isFileUpload, prompt } = inputs;
let result = { answer: '', model: model, modelName: modelName, chatId: chatId, fileInfo: '', status: 2 };
console.log('开始处理', { model, modelName, prompt });
const options = {
method: 'POST',
url: `${proxyUrl}/v1/chat/completions`,
timeout: timeout,
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${apiKey}`,
},
data: {
model,
messages: [{ "role": "user", "content": prompt }],
},
};
try {
const response = await (0, axios_1.default)(options);
console.log('API响应接收', response.data);
if (response.data.choices && response.data.choices.length > 0) {
const choice = response.data.choices[0];
const content = choice.message.content;
console.log('处理内容', content);
const regex = /\]\((https?:\/\/[^\)]+)\)/;
const match = content.match(regex);
if (match && match[1]) {
result.fileInfo = match[1];
console.log('找到链接', match[1]);
}
else {
console.log('没有找到链接');
}
let revised_prompt_cn;
result.answer = `${prompt} 绘制成功`;
if (result.fileInfo) {
onSuccess(result);
return;
}
else {
onFailure("No link found.");
}
}
else {
onFailure("No choices returned.");
}
}
catch (error) {
common_1.Logger.error('服务器错误,请求失败:', error);
}
}
async suno(messagesHistory, inputs) {
common_1.Logger.log('开始生成歌曲');
const { onGenerate, onFailure, onSuccess, apiKey, model, proxyUrl, timeout, prompt } = inputs;
let result = { answer: '', fileInfo: '', errMsg: '' };
let fullText = '';
const options = {
method: 'POST',
url: `${proxyUrl}/v1/chat/completions`,
responseType: "stream",
timeout: timeout,
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${apiKey}`,
},
data: {
stream: true,
model,
messages: [{
"role": "user",
"content": prompt
}],
},
};
try {
const response = await (0, axios_1.default)(options);
const stream = response.data;
await new Promise((resolve, reject) => {
stream.on('data', (chunk) => {
common_1.Logger.log('生成进度: ', fullText);
const splitArr = chunk.toString().split('\n\n').filter(line => line.trim());
splitArr.forEach(line => {
var _a, _b;
const videoLinkMatch = fullText.match(/\((https?:\/\/[^\)]+\.mp4)\)/);
if (line.trim() === "data: [DONE]" || videoLinkMatch) {
if (videoLinkMatch) {
result.fileInfo = videoLinkMatch[1];
onSuccess(result);
return;
}
return;
}
try {
const jsonLine = JSON.parse(line.replace(/^data: /, '').trim());
const content = ((_b = (_a = jsonLine.choices[0]) === null || _a === void 0 ? void 0 : _a.delta) === null || _b === void 0 ? void 0 : _b.content) || '';
fullText += content;
if (!fullText.includes('### 🎵')) {
if (fullText.includes('生成中..')) {
result.answer = '歌曲生成中';
onGenerate(result);
}
else if (fullText.includes('排队中.')) {
result.answer = '排队中';
onGenerate(result);
}
else {
result.answer = '提交成功,歌曲生成中';
onGenerate(result);
}
}
else if (!fullText.includes('**风格:**')) {
const startLyricsIndex = fullText.indexOf('### 🎵');
result.answer = fullText.substring(startLyricsIndex);
onGenerate(result);
}
else {
const startLyricsIndex = fullText.indexOf('### 🎵');
const endStyleIndex = fullText.indexOf('**风格:**');
result.answer = fullText.substring(startLyricsIndex, endStyleIndex);
onGenerate(result);
}
const videoLinkMatch = fullText.match(/\((https?:\/\/[^\)]+\.mp4)\)/);
if (videoLinkMatch) {
result.fileInfo = videoLinkMatch[1];
onSuccess(result);
return;
}
}
catch (error) {
console.error('Parse error', error, line);
}
});
});
stream.on('end', () => {
common_1.Logger.log('Stream ended');
resolve(result);
});
stream.on('error', (error) => {
common_1.Logger.error('Stream error:', error);
reject(error);
});
});
return result;
}
catch (error) {
result.errMsg = await this.handleError(error);
common_1.Logger.error(result.errMsg);
onFailure(result);
return;
}
}
async sendMessageFromAi(messagesHistory, inputs, isClientClosed) {
const { onFailure, onProgress, apiKey, model, proxyUrl, modelName, timeout, chatId, isFileUpload, modelAvatar, temperature, } = inputs;
let result = {
text: '',
model: '',
modelName: modelName,
chatId: chatId,
answer: '',
errMsg: '',
modelAvatar: modelAvatar,
};
const options = {
method: 'POST',
url: `${proxyUrl}/v1/chat/completions`,
responseType: 'stream',
timeout: timeout,
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${apiKey}`,
},
data: Object.assign({ stream: true, model, temperature: temperature, messages: messagesHistory }, (isFileUpload === 2 && { max_tokens: 2048 })),
};
try {
const response = await (0, axios_1.default)(options);
const stream = response.data;
await new Promise((resolve, reject) => {
stream.on('data', (chunk) => {
const pattern = /(?<=\})(\n\n)(?=data)/g;
const splitArr = chunk
.toString()
.split(pattern)
.filter((line) => line.trim());
// common_1.Logger.log('splitArr', splitArr);
splitArr.forEach((line) => {
var _a, _b;
if (line.trim() === 'data: [DONE]') {
console.log('处理结束信号 [DONE]');
resolve(result);
return;
}
try {
const cleanedLine = line.replace(/^data: /, '').trim();
if (!cleanedLine)
return;
const jsonLine = JSON.parse(cleanedLine);
if (jsonLine) {
const content = ((_b = (_a = jsonLine.choices[0]) === null || _a === void 0 ? void 0 : _a.delta) === null || _b === void 0 ? void 0 : _b.content) || '';
result.answer += content;
onProgress === null || onProgress === void 0 ? void 0 : onProgress({ text: content, answer: result.answer });
}
}
catch (error) {
const contentMatch = line.match(/"content":"([^"]+)"/);
if (contentMatch && contentMatch[1]) {
result.answer += contentMatch[1];
onProgress === null || onProgress === void 0 ? void 0 : onProgress({ text: contentMatch[1], answer: result.answer });
}
}
});
});
stream.on('error', reject);
});
return result;
}
catch (error) {
result.errMsg = await this.handleError(error);
common_1.Logger.error(result.errMsg);
onFailure(result);
return result;
}
}
async handleError(error) {
let message = '发生未知错误,请稍后再试';
if (axios_1.default.isAxiosError(error) && error.response) {
switch (error.response.status) {
case 400:
message = '发生错误400 Bad Request - 请求因格式错误无法被服务器处理。';
break;
case 401:
message = '发生错误401 Unauthorized - 请求要求进行身份验证。';
break;
case 403:
message = '发生错误403 Forbidden - 服务器拒绝执行请求。';
break;
case 404:
message = '发生错误404 Not Found - 请求的资源无法在服务器上找到。';
break;
case 500:
message = '发生错误500 Internal Server Error - 服务器内部错误,无法完成请求。';
break;
case 502:
message = '发生错误502 Bad Gateway - 作为网关或代理工作的服务器从上游服务器收到无效响应。';
break;
case 503:
message = '发生错误503 Service Unavailable - 服务器暂时处于超负载或维护状态,无法处理请求。';
break;
default:
break;
}
}
else {
message = error.message || message;
}
return message;
}
async getTokenCount(text) {
if (!text)
return 0;
if (typeof text !== 'string') {
text = String(text);
}
text = text.replace(/<\|endoftext\|>/g, '');
const tokenizer = (0, tiktoken_1.get_encoding)('cl100k_base');
return tokenizer.encode(text).length;
}
async mjDraw(inputs) {
var _a, _b;
const { id, apiKey, proxyUrl, action, drawId, prompt, usePrompt, customId, timeout, assistantLogId } = inputs;
let result = { text: '', fileInfo: '', drawId: '', customId: '', status: 2 };
let response;
let retryCount = 0;
let url = '';
while (retryCount < 3) {
let payloadJson = {};
try {
if (action === 'IMAGINE') {
url = `${proxyUrl}/mj/submit/imagine`;
payloadJson = { prompt: usePrompt };
}
else {
url = `${proxyUrl}/mj/submit/action`;
payloadJson = { taskId: drawId, customId: customId };
}
const headers = { "mj-api-secret": apiKey };
common_1.Logger.debug(`正在准备发送请求到 ${url}payload: ${JSON.stringify(payloadJson)}, headers: ${JSON.stringify(headers)}`);
response = await axios_1.default.post(url, payloadJson, { headers });
common_1.Logger.debug(`任务提交结果,状态码: ${response.status}, 状态消息: ${response.statusText}, 数据: ${JSON.stringify(response.data)}`);
if ((_a = response === null || response === void 0 ? void 0 : response.data) === null || _a === void 0 ? void 0 : _a.result) {
result.drawId = (_b = response === null || response === void 0 ? void 0 : response.data) === null || _b === void 0 ? void 0 : _b.result;
break;
}
else {
throw new Error('未能获取结果数据, 即将重试');
}
}
catch (error) {
retryCount++;
if (retryCount >= 3) {
common_1.Logger.log(`绘画任务提交失败, 请检查后台配置或者稍后重试! ${error}`, 'MidjourneyService');
}
}
}
this.pollMjDrawingResult({
proxyUrl,
apiKey,
drawId: response.data.result,
timeout,
prompt,
onSuccess: async (data) => {
await this.chatLogService.updateChatLog(assistantLogId, {
fileInfo: data === null || data === void 0 ? void 0 : data.fileInfo,
answer: (data === null || data === void 0 ? void 0 : data.answer) || prompt,
progress: '100%',
status: 3,
drawId: data === null || data === void 0 ? void 0 : data.drawId,
customId: data === null || data === void 0 ? void 0 : data.customId,
});
common_1.Logger.log('绘图成功!');
},
onDrawing: async (data) => {
await this.chatLogService.updateChatLog(assistantLogId, {
answer: (data === null || data === void 0 ? void 0 : data.answer) || '绘制中',
progress: data === null || data === void 0 ? void 0 : data.progress,
status: 2,
});
common_1.Logger.log(`绘制中!绘制进度${data === null || data === void 0 ? void 0 : data.progress}`);
},
onFailure: async (data) => {
await this.chatLogService.updateChatLog(assistantLogId, {
answer: '绘图失败',
status: data.status,
});
common_1.Logger.log('绘图失败');
}
}).catch(error => {
common_1.Logger.error("查询绘图结果时发生错误:", error, 'MidjourneyService');
});
common_1.Logger.log(`绘画任务提交成功, 绘画ID: ${response.data.result}`, 'MidjourneyService');
return result;
}
async pollMjDrawingResult(inputs) {
const { proxyUrl, apiKey, drawId, timeout, onSuccess, prompt, onFailure, onDrawing } = inputs;
const { mjNotSaveImg, mjProxyImgUrl, mjNotUseProxy, } = await this.globalConfigService.getConfigs([
'mjNotSaveImg',
'mjProxyImgUrl',
'mjNotUseProxy',
]);
let response;
let result = { fileInfo: '', drawId: '', customId: '', status: 2, progress: 0, answer: '' };
let payloadJson = {};
const startTime = Date.now();
const POLL_INTERVAL = 5000;
let retryCount = 0;
let pollingCount = 0;
try {
while (Date.now() - startTime < timeout) {
await new Promise(resolve => setTimeout(resolve, POLL_INTERVAL));
try {
const headers = {
"Content-Type": "application/x-www-form-urlencoded",
"mj-api-secret": apiKey
};
const url = `${proxyUrl}/mj/task/${drawId}/fetch`;
const res = await axios_1.default.get(url, { headers });
const responses = res.data;
if (responses.status === 'SUCCESS') {
common_1.Logger.log(`绘制成功, 获取到的URL: ${responses.imageUrl}`, 'MidjourneyService');
let processedUrl = responses.imageUrl;
const shouldReplaceUrl = mjNotUseProxy === '0' && mjProxyImgUrl;
let logMessage = '';
if (shouldReplaceUrl) {
const newUrlBase = new URL(mjProxyImgUrl);
const parsedUrl = new URL(responses.imageUrl);
parsedUrl.protocol = newUrlBase.protocol;
parsedUrl.hostname = newUrlBase.hostname;
parsedUrl.port = newUrlBase.port ? newUrlBase.port : '';
processedUrl = parsedUrl.toString();
logMessage = `使用代理替换后的 URL: ${processedUrl}`;
common_1.Logger.log(logMessage, 'MidjourneyService');
}
if (mjNotSaveImg !== '1') {
try {
common_1.Logger.debug(`------> 开始上传图片!!!`);
const filename = `${Date.now()}-${uuid.v4().slice(0, 4)}.png`;
processedUrl = await this.uploadService.uploadFileFromUrl({ filename, url: processedUrl });
logMessage = `上传成功 URL: ${processedUrl}`;
}
catch (uploadError) {
common_1.Logger.error('存储图片失败,使用原始/代理图片链接');
logMessage = `存储图片失败,使用原始/代理图片链接 ${processedUrl}`;
}
common_1.Logger.log(logMessage, 'MidjourneyService');
}
else {
logMessage = `不保存图片,使用 URL: ${processedUrl}`;
common_1.Logger.log(logMessage, 'MidjourneyService');
}
result.fileInfo = processedUrl;
result.drawId = responses.id;
result.customId = JSON.stringify(responses.buttons);
result.answer = `${prompt}\n${responses.finalPrompt || responses.properties.finalPrompt || ''}`;
onSuccess(result);
return;
}
result.progress = responses === null || responses === void 0 ? void 0 : responses.progress;
result.answer = `当前绘制进度 ${responses === null || responses === void 0 ? void 0 : responses.progress}`;
if (result.progress) {
onDrawing(result);
}
}
catch (error) {
retryCount++;
common_1.Logger.error(`轮询过程中发生错误: ${error}`, 'MidjourneyService');
}
}
common_1.Logger.error(`超过 ${startTime / 1000} s 未完成绘画, 请稍后再试! MidjourneyService`);
result.status = 4;
onFailure(result);
throw new common_1.HttpException('绘画超时,请稍后再试!', common_1.HttpStatus.BAD_REQUEST);
}
catch (error) {
common_1.Logger.error(`绘画失败: ${error} MidjourneyService`);
result.status = 5;
onFailure(result);
}
}
};
ChatService = __decorate([
(0, common_1.Injectable)(),
__param(0, (0, typeorm_1.InjectRepository)(config_entity_1.ConfigEntity)),
__param(1, (0, typeorm_1.InjectRepository)(chatBoxType_entity_1.ChatBoxTypeEntity)),
__param(2, (0, typeorm_1.InjectRepository)(chatBox_entity_1.ChatBoxEntity)),
__param(3, (0, typeorm_1.InjectRepository)(app_entity_1.AppEntity)),
__param(4, (0, typeorm_1.InjectRepository)(chatPreType_entity_1.ChatPreTypeEntity)),
__param(5, (0, typeorm_1.InjectRepository)(chatPre_entity_1.ChatPreEntity)),
__metadata("design:paramtypes", [typeorm_2.Repository,
typeorm_2.Repository,
typeorm_2.Repository,
typeorm_2.Repository,
typeorm_2.Repository,
typeorm_2.Repository,
apiDataService_service_1.ApiDataService,
chatLog_service_1.ChatLogService,
nestjs_config_1.ConfigService,
userBalance_service_1.UserBalanceService,
user_service_1.UserService,
upload_service_1.UploadService,
badwords_service_1.BadwordsService,
autoreply_service_1.AutoreplyService,
globalConfig_service_1.GlobalConfigService,
chatGroup_service_1.ChatGroupService,
models_service_1.ModelsService])
], ChatService);
exports.ChatService = ChatService;