mirror of
https://github.com/vastxie/99AI.git
synced 2025-09-17 17:26:38 +08:00
1300 lines
64 KiB
JavaScript
1300 lines
64 KiB
JavaScript
"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;
|