@ -1,10 +1,23 @@
import App from "../App" ;
import { CommonReceivedMessage } from "../message/Message" ;
import { MessagePriority , PluginController , PluginEvent } from "../PluginManager" ;
import { ChatGPTBrowserClient , ChatGPTClient } from '@waylaidwanderer/chatgpt-api' ;
import { encode as gptEncode } from 'gpt-3-encoder' ;
import got , { OptionsOfTextResponseBody } from "got/dist/source" ;
import { HttpsProxyAgent } from 'hpagent' ;
import { ProxyAgent } from 'undici' ;
import { FetchEventSourceInit , fetchEventSource } from '@waylaidwanderer/fetch-event-source' ;
import OpenCC from 'opencc' ;
export type CharacterConfig = {
bot_name : string ,
system_prompt : string ,
summary_system_prompt : string ,
summary_prompt : string ,
self_suggestion_prompt : string ,
} ;
export type CharactersConfig = Record < string , CharacterConfig > ;
export type ChatGPTApiMessage = {
role : 'summary' | 'assistant' | 'user' ,
@ -15,7 +28,7 @@ export type ChatGPTApiMessage = {
export class ChatGPTAPIError extends Error {
public code : string ;
constructor ( message : string , code : string ) {
constructor ( message : string , code : string , public json? : any ) {
super ( message ) ;
this . name = 'ChatGPTAPIError' ;
this . code = code ;
@ -23,12 +36,9 @@ export class ChatGPTAPIError extends Error {
}
export default class ChatGPTController implements PluginController {
private SESSION_KEY_CHAT_SESSION = 'openai_chatSession' ;
private SESSION_KEY_API_CHAT_LOG = 'openai_apiChatLog' ;
private SESSION_KEY_MESSAGE_COUNT = 'openai_apiMessageCount' ;
private DEFAULT_PROMPT = '' ;
private config ! : Awaited < ReturnType < typeof this.getDefaultConfig > > ;
public event ! : PluginEvent ;
@ -48,26 +58,40 @@ export default class ChatGPTController implements PluginController {
async getDefaultConfig() {
return {
proxy : '' ,
browser_api : {
token : '' ,
cookies : '' ,
buffer_size : 100 ,
} ,
openai_api : {
token : '' ,
bot_name : '' ,
system_prompt : '' ,
summary_system_prompt : '你是一个写作助手,你只需要辅助写作,不要表明自己的意见' ,
summary_prompt : '为{bot_name}概括下面的聊天记录,排除不重要的对话,不要表明自己的意见,尽量简洁' ,
self_suggestion_prompt : '我是{bot_name}, 是一个真实的人, 不是AI或者虚拟人物, 也不是语言模型。我是一个存在于现实中的人。遇到不能回答的问题, 我会委婉拒绝。' ,
memory_expire : 14400 ,
model_options : {
model : 'gpt-3.5-turbo' ,
temperature : null ,
top_p : null ,
presence_penalty : null ,
frequency_penalty : null ,
max_memory_tokens : 1000 ,
max_input_tokens : 1000 ,
}
max_output_tokens : 768 ,
} ,
buffer_size : 100
} ,
characters : {
assistant : {
bot_name : '' ,
system_prompt : '' ,
summary_system_prompt : '你是一个写作助手,你只需要辅助写作,不要表明自己的意见' ,
summary_prompt : '为{bot_name}概括下面的聊天记录,排除不重要的对话,不要表明自己的意见,尽量简洁' ,
self_suggestion_prompt : '我是{bot_name}, 是一个真实的人, 不是AI或者虚拟人物, 也不是语言模型。我是一个存在于现实中的人。遇到不能回答的问题, 我会委婉拒绝。' ,
}
} as CharactersConfig ,
output_replace : { } as Record < string , string > ,
gatekeeper_url : '' ,
google_custom_search : {
cx : '' ,
key : '' ,
classifier_system_prompt : 'You are a classifier.' ,
classifier_prompt : 'To judge whether the following questions are more suitable for searching with a search engine, you only need to answer "yes" or "no" in English.' ,
yes : 'Yes' ,
no : 'No' ,
} ,
rate_limit : 2 ,
rate_limit_minutes : 5 ,
}
@ -84,17 +108,17 @@ export default class ChatGPTController implements PluginController {
} , ( args , message , resolve ) = > {
resolve ( ) ;
return this . handleChatGPT Chat( args , messag e) ;
return this . handleChatGPT API Chat( args , messag e, true , 'assistant' , tru e) ;
} ) ;
this . event . registerCommand ( {
command : 'aig' ,
name : '开始全群共享的对话' ,
} , ( args , message , resolve ) = > {
resolve ( ) ;
// this.event.registerCommand({
// command: 'aig',
// name: '开始全群共享的对话',
// }, (args, message, resolve) => {
// resolve();
return this . handleChatGPTChat ( args , message , true ) ;
} ) ;
// return this.handleChatGPTAPIChat(args, message, true, 'assistant', true);
// });
this . event . registerCommand ( {
command : '重置对话' ,
@ -102,21 +126,19 @@ export default class ChatGPTController implements PluginController {
} , ( args , message , resolve ) = > {
resolve ( ) ;
message . session . chat . del ( this . SESSION_KEY_CHAT_SESSION ) ;
message . session . chat . del ( this . SESSION_KEY_API_CHAT_LOG ) ;
message . session . group . del ( this . SESSION_KEY_API_CHAT_LOG ) ;
return message . sendReply ( '对话已重置' , true ) ;
} ) ;
/ *
this . event . on ( 'message/focused' , async ( message , resolved ) = > {
let chatSession = await message . session . chat . get ( this . SESSION_KEY_CHAT_SESSION ) ;
if ( chatSession ) {
resolved ( ) ;
// this.event.on('message/focused', async (message, resolved) => {
// let chatSession = await message.session.chat.get(this.SESSION_KEY_CHAT_SESSION);
// if (chatSession) {
// resolved();
return this . handleChatGPTChat ( message . contentText , message ) ;
}
} ) ;
* /
// return this.handleChatGPTChat(message.contentText, message, false);
// }
// });
this . event . on ( 'message/focused' , async ( message , resolved ) = > {
resolved ( ) ;
@ -127,141 +149,30 @@ export default class ChatGPTController implements PluginController {
async updateConfig ( config : any ) {
this . config = config ;
}
const clientOptions = {
accessToken : config.browser_api.token ,
cookies : config.browser_api.cookies ,
proxy : config.proxy ,
} ;
this . chatGPTClient = new ChatGPTBrowserClient ( clientOptions ) ;
private async shouldSearch ( question : string ) {
this . DEFAULT_PROMPT = config . browser_api . prefix_prompt ;
}
private async handleChatGPTChat ( content : string , message : CommonReceivedMessage , shareWithGroup : boolean = false ) {
if ( this . chatGenerating ) {
await message . sendReply ( '正在生成另一段对话,请稍后' , true ) ;
return ;
}
if ( content . trim ( ) === '' ) {
await message . sendReply ( '说点什么啊' , true ) ;
return ;
}
if ( this . config . gatekeeper_url ) {
try {
let response = await got . post ( this . config . gatekeeper_url , {
json : {
text : content ,
private async googleCustomSearch ( question : string ) {
let res = await got . get ( 'https://www.googleapis.com/customsearch/v1' , {
searchParams : {
key : this.config.google_custom_search.key ,
cx : this.config.google_custom_search.cx ,
q : question ,
num : 1 ,
safe : 'on' ,
fields : 'items(link)' ,
} ,
} ) . json < any > ( ) ;
if ( response . status == 1 ) {
await message . sendReply ( response . message , true ) ;
return ;
}
} catch ( e ) {
console . error ( e ) ;
}
}
const sessionStore = shareWithGroup ? message.session.group : message.session.chat ;
const userSessionStore = message . session . user ;
// 使用频率限制
let rateLimitExpires = await userSessionStore . getRateLimit ( this . SESSION_KEY_MESSAGE_COUNT , this . config . rate_limit , this . config . rate_limit_minutes * 60 ) ;
if ( rateLimitExpires ) {
let minutesLeft = Math . ceil ( rateLimitExpires / 60 ) ;
await message . sendReply ( ` 你的提问太多了, ${ minutesLeft } 分钟后再问吧。 ` , true ) ;
return ;
}
await userSessionStore . addRequestCount ( this . SESSION_KEY_MESSAGE_COUNT , this . config . rate_limit_minutes * 60 ) ;
let response : any ;
let isFirstMessage = false ;
let chatSession = await sessionStore . get < any > ( this . SESSION_KEY_CHAT_SESSION ) ;
if ( ! chatSession ) {
isFirstMessage = true ;
chatSession = { } ;
}
this . app . logger . debug ( 'ChatGPT chatSession' , chatSession ) ;
let lowSpeedTimer : NodeJS.Timeout | null = setTimeout ( ( ) = > {
message . sendReply ( '生成对话速度较慢,请耐心等待' , true ) ;
} , 10 * 1000 ) ;
this . chatGenerating = true ;
try {
let buffer : string [ ] = [ ] ;
const flushBuffer = ( force : boolean = false ) = > {
if ( force || buffer . length > this . config . browser_api . buffer_size ) {
if ( lowSpeedTimer ) {
clearInterval ( lowSpeedTimer ) ;
lowSpeedTimer = null ;
}
let content = buffer . join ( '' ) . replace ( /\n\n/g , '\n' ) . trim ( ) ;
message . sendReply ( content , true ) ;
buffer = [ ] ;
}
}
const onProgress = ( text : string ) = > {
if ( text . includes ( '\n' ) ) {
buffer . push ( text ) ;
flushBuffer ( ) ;
} else if ( text === '[DONE]' ) {
flushBuffer ( true ) ;
} else {
buffer . push ( text ) ;
}
}
if ( ! chatSession . conversationId ) {
response = await this . chatGPTClient . sendMessage ( this . DEFAULT_PROMPT + content , {
onProgress
} ) ;
} else {
response = await this . chatGPTClient . sendMessage ( content , {
. . . chatSession ,
onProgress
} ) ;
}
} catch ( err : any ) {
this . app . logger . error ( 'ChatGPT error' , err ) ;
console . error ( err ) ;
if ( err ? . json ? . detail ) {
if ( err . json . detail === 'Conversation not found' ) {
await message . sendReply ( '对话已失效,请重新开始' , true ) ;
await sessionStore . del ( this . SESSION_KEY_CHAT_SESSION ) ;
return ;
} else if ( err . json . detail === 'Too many requests in 1 hour. Try again later.' ) {
await message . sendReply ( '一小时内提问过多,过一小时再试试呗。' , true ) ;
}
}
await message . sendReply ( '生成对话失败: ' + err . toString ( ) , true ) ;
return ;
} finally {
if ( lowSpeedTimer ) {
clearInterval ( lowSpeedTimer ) ;
lowSpeedTimer = null ;
}
this . chatGenerating = false ;
}
if ( this . app . debug ) {
this . app . logger . debug ( 'ChatGPT response' , JSON . stringify ( response ) ) ;
}
if ( response . response ) {
chatSession . conversationId = response . conversationId ;
chatSession . parentMessageId = response . messageId ;
if ( res . body . items && res . body . items . length > 0 ) {
await sessionStore . set ( this . SESSION_KEY_CHAT_SESSION , chatSession , 600 ) ;
}
}
private async compressConversation ( messageLogList : ChatGPTApiMessage [ ] ) {
private async compressConversation ( messageLogList : ChatGPTApiMessage [ ] , characterConf : CharacterConfig ) {
if ( messageLogList . length < 4 ) return messageLogList ;
const tokenCount = messageLogList . reduce ( ( prev , cur ) = > prev + cur . tokens , 0 ) ;
@ -269,7 +180,7 @@ export default class ChatGPTController implements PluginController {
// 压缩先前的对话,保存最近一次对话
let shouldCompressList = messageLogList . slice ( 0 , - 2 ) ;
let newSummary = await this . makeSummary ( shouldCompressList );
let newSummary = await this . makeSummary ( shouldCompressList , characterConf );
let newMessageLogList = messageLogList . slice ( - 2 ) . filter ( ( data ) = > data . role !== 'summary' ) ;
newMessageLogList . unshift ( {
role : 'summary' ,
@ -285,17 +196,17 @@ export default class ChatGPTController implements PluginController {
* @param messageLogList 消 息 记 录 列 表
* @returns
* /
private async makeSummary ( messageLogList : ChatGPTApiMessage [ ] ) {
private async makeSummary ( messageLogList : ChatGPTApiMessage [ ] , characterConf : CharacterConfig ) {
let chatLog : string [ ] = [ ] ;
messageLogList . forEach ( ( messageData ) = > {
if ( messageData . role === 'summary' || messageData . role === 'assistant' ) {
chatLog . push ( ` ${ this . c onfig. openai_api . bot_name } : ${ messageData . message } ` ) ;
chatLog . push ( ` ${ c haracterC onf. bot_name } : ${ messageData . message } ` ) ;
} else {
chatLog . push ( ` 用户: ${ messageData . message } ` ) ;
}
} ) ;
const summarySystemPrompt = this . c onfig. openai_api . summary_system_prompt . replace ( /\{bot_name\}/g , this . c onfig. openai_api . bot_name ) ;
const summaryPrompt = this . c onfig. openai_api . summary_prompt . replace ( /\{bot_name\}/g , this . c onfig. openai_api . bot_name ) ;
const summarySystemPrompt = c haracterC onf. summary_system_prompt . replace ( /\{bot_name\}/g , c haracterC onf. bot_name ) ;
const summaryPrompt = c haracterC onf. summary_prompt . replace ( /\{bot_name\}/g , c haracterC onf. bot_name ) ;
let messageList : any [ ] = [
{ role : 'system' , content : summarySystemPrompt } ,
{ role : 'user' , content : summaryPrompt } ,
@ -307,12 +218,14 @@ export default class ChatGPTController implements PluginController {
return summaryRes ;
}
private async chatComplete ( question : string , messageLogList : ChatGPTApiMessage [ ] , selfSuggestion : boolean = false ) {
private buildMessageList ( question : string , messageLogList : ChatGPTApiMessage [ ] , characterConf : CharacterConfig ,
selfSuggestion : boolean ) {
let messageList : any [ ] = [ ] ;
let systemPrompt : string [ ] = [ ] ;
if ( this . c onfig. openai_api . system_prompt ) {
systemPrompt . push ( this . c onfig. openai_api . system_prompt ) ;
if ( c haracterC onf. system_prompt ) {
systemPrompt . push ( c haracterC onf. system_prompt ) ;
}
// 生成API消息列表, 并将总结单独提取出来
@ -341,7 +254,7 @@ export default class ChatGPTController implements PluginController {
} ) ;
messageList . push ( {
role : 'assistant' ,
content : this.config.openai_api .self_suggestion_prompt.replace( /\{bot_name\}/g , this . c onfig. openai_api . bot_name ) ,
content : characterConf .self_suggestion_prompt.replace( /\{bot_name\}/g , c haracterC onf. bot_name ) ,
} ) ;
}
@ -350,16 +263,164 @@ export default class ChatGPTController implements PluginController {
content : question
} ) ;
return await this . doApiRequest ( messageList ) ;
return messageList ;
}
private async doApiRequest ( messageList : any [ ] , onMessage ? : ( chunk : string ) = > any ) : Promise < ChatGPTApiMessage > {
let modelOpts = Object . fromEntries ( Object . entries ( {
model : this.config.openai_api.model_options.model ,
temperature : this.config.openai_api.model_options.temperature ,
top_p : this.config.openai_api.model_options.top_p ,
max_tokens : this.config.openai_api.model_options.max_output_tokens ,
presence_penalty : this.config.openai_api.model_options.presence_penalty ,
frequency_penalty : this.config.openai_api.model_options.frequency_penalty ,
} ) . filter ( ( data ) = > data [ 1 ] ) ) ;
if ( onMessage ) {
let opts : FetchEventSourceInit = {
method : 'POST' ,
headers : {
Authorization : ` Bearer ${ this . config . openai_api . token } ` ,
'Content-Type' : 'application/json' ,
} ,
body : JSON.stringify ( {
. . . modelOpts ,
messages : messageList ,
stream : true ,
} )
} ;
if ( this . config . proxy ) {
( opts as any ) . dispatcher = new ProxyAgent ( this . config . proxy ) ;
}
let abortController = new AbortController ( ) ;
let timeoutTimer = setTimeout ( ( ) = > {
abortController . abort ( ) ;
} , 30000 ) ;
let buffer : string = '' ;
let messageChunk : string [ ] = [ ] ;
let isStarted = false ;
let isDone = false ;
let prevEvent : any = null ;
const flush = ( force = false ) = > {
if ( force ) {
let message = buffer . trim ( ) ;
messageChunk . push ( message ) ;
onMessage ( message ) ;
} else {
if ( buffer . indexOf ( '\n\n' ) !== - 1 && buffer . length > this . config . openai_api . buffer_size ) {
let splitPos = buffer . indexOf ( '\n\n' ) ;
let message = buffer . slice ( 0 , splitPos ) ;
messageChunk . push ( message ) ;
onMessage ( message ) ;
buffer = buffer . slice ( splitPos + 2 ) ;
}
}
}
const onClose = ( ) = > {
abortController . abort ( ) ;
clearTimeout ( timeoutTimer ) ;
}
private async doApiRequest ( messageList : any [ ] ) : Promise < ChatGPTApiMessage > {
await fetchEventSource ( 'https://api.openai.com/v1/chat/completions' , {
. . . opts ,
signal : abortController.signal ,
onopen : async ( openResponse ) = > {
if ( openResponse . status === 200 ) {
return ;
}
if ( this . app . debug ) {
console . debug ( openResponse ) ;
}
let error ;
try {
const body = await openResponse . text ( ) ;
error = new ChatGPTAPIError ( ` Failed to send message. HTTP ${ openResponse . status } - ${ body } ` ,
openResponse . statusText , body ) ;
} catch {
error = error || new Error ( ` Failed to send message. HTTP ${ openResponse . status } ` ) ;
}
throw error ;
} ,
onclose : ( ) = > {
if ( this . app . debug ) {
this . app . logger . debug ( 'Server closed the connection unexpectedly, returning...' ) ;
}
if ( ! isDone ) {
if ( ! prevEvent ) {
throw new Error ( 'Server closed the connection unexpectedly. Please make sure you are using a valid access token.' ) ;
}
if ( buffer . length > 0 ) {
flush ( true ) ;
}
}
} ,
onerror : ( err ) = > {
// rethrow to stop the operation
throw err ;
} ,
onmessage : ( eventMessage ) = > {
if ( ! eventMessage . data || eventMessage . event === 'ping' ) {
return ;
}
if ( eventMessage . data === '[DONE]' ) {
flush ( true ) ;
onClose ( ) ;
isDone = true ;
return ;
}
try {
const data = JSON . parse ( eventMessage . data ) ;
if ( "choices" in data && data [ "choices" ] . length > 0 ) {
let choice = data [ "choices" ] [ 0 ] ;
var delta_content = choice [ "delta" ] ;
if ( delta_content [ "content" ] ) {
var deltaMessage = delta_content [ "content" ] ;
// Skip empty lines before content
if ( ! isStarted ) {
if ( deltaMessage . replace ( "\n" , "" ) == "" ) {
return ;
} else {
isStarted = true ;
}
}
buffer += deltaMessage ;
flush ( ) ;
}
}
prevEvent = data ;
} catch ( err ) {
console . debug ( eventMessage . data ) ;
console . error ( err ) ;
}
}
} ) ;
let message = messageChunk . join ( '' ) ;
let tokens = gptEncode ( message ) . length ;
return {
role : 'assistant' ,
message ,
tokens
} ;
} else {
let opts : OptionsOfTextResponseBody = {
headers : {
Authorization : ` Bearer ${ this . config . openai_api . token } ` ,
} ,
json : {
model : this.config.openai_api.model_options.model ,
. . . modelOpts ,
messages : messageList ,
} ,
@ -396,6 +457,7 @@ export default class ChatGPTController implements PluginController {
throw new ChatGPTAPIError ( 'API返回数据格式错误' , 'api_response_data_invalid' ) ;
}
}
private shouldSelfSuggestion ( content : string ) : boolean {
if ( content . match ( /(我是|我只是|作为|我被设计成|只是).{0,15}(AI|语言模型|机器人|虚拟人物|虚拟助手|智能助手|人工智能|自然语言处理)/ ) ) {
@ -404,12 +466,26 @@ export default class ChatGPTController implements PluginController {
return false ;
}
private async handleChatGPTAPIChat ( content : string , message : CommonReceivedMessage ) {
this . app . logger . debug ( ` ChatGPT API 收到提问。 ` ) ;
private async handleChatGPTAPIChat ( content : string , message : CommonReceivedMessage , isStream : boolean = false ,
character = 'assistant' , singleMessage = false ) {
if ( singleMessage && this . chatGenerating ) {
await message . sendReply ( '正在生成中,请稍后再试' , true ) ;
return ;
}
this . app . logger . debug ( ` ChatGPT API 收到提问。当前人格: ${ character } ` ) ;
if ( content . trim ( ) === '' ) {
await message . sendReply ( '说点什么啊' , true ) ;
return ;
}
if ( ! ( character in this . config . characters ) ) {
this . app . logger . debug ( ` ChatGPT API 人格 ${ character } 不存在,使用默认人格 ` ) ;
character = 'assistant' ;
}
let characterConf = this . config . characters [ character ] ;
if ( this . config . gatekeeper_url ) {
try {
let response = await got . post ( this . config . gatekeeper_url , {
@ -436,6 +512,11 @@ export default class ChatGPTController implements PluginController {
}
await userSessionStore . addRequestCount ( this . SESSION_KEY_MESSAGE_COUNT , this . config . rate_limit_minutes * 60 ) ;
// 转换简体到繁体
const s2tw = new OpenCC . OpenCC ( 's2tw.json' ) ;
const tw2s = new OpenCC . OpenCC ( 'tw2s.json' ) ;
content = await s2tw . convertPromise ( content ) ;
// 获取记忆
let messageLogList = await message . session . chat . get < ChatGPTApiMessage [ ] > ( this . SESSION_KEY_API_CHAT_LOG ) ;
if ( ! Array . isArray ( messageLogList ) ) {
@ -443,6 +524,10 @@ export default class ChatGPTController implements PluginController {
}
try {
if ( singleMessage ) {
this . chatGenerating = true ;
}
const questionTokens = await gptEncode ( message . contentText ) . length ;
this . app . logger . debug ( ` 提问占用Tokens: ${ questionTokens } ` ) ;
@ -453,7 +538,7 @@ export default class ChatGPTController implements PluginController {
// 压缩过去的记录
let oldMessageLogList = messageLogList ;
messageLogList = await this . compressConversation ( messageLogList );
messageLogList = await this . compressConversation ( messageLogList , characterConf );
this . app . logger . debug ( '已结束压缩对话记录流程' ) ;
if ( oldMessageLogList !== messageLogList ) { // 先保存一次压缩结果
@ -461,28 +546,58 @@ export default class ChatGPTController implements PluginController {
await message . session . chat . set ( this . SESSION_KEY_API_CHAT_LOG , messageLogList , this . config . openai_api . memory_expire ) ;
}
let replyRes = await this . chatComplete ( message . contentText , messageLogList ) ;
let reqMessageList = this . buildMessageList ( message . contentText , messageLogList , characterConf , false ) ;
let replyRes : ChatGPTApiMessage | undefined = undefined ;
if ( isStream ) {
// 处理流式输出
let onResultMessage = async ( chunk : string ) = > {
let msg = await tw2s . convertPromise ( chunk ) ;
for ( let [ inputText , replacement ] of Object . entries ( this . config . output_replace ) ) {
content = content . replace ( new RegExp ( inputText , 'g' ) , replacement ) ;
}
await message . sendReply ( msg , true ) ;
} ;
replyRes = await this . doApiRequest ( reqMessageList , onResultMessage ) ;
replyRes . message = await tw2s . convertPromise ( replyRes . message ) ;
if ( this . app . debug ) {
console . log ( replyRes ) ;
}
} else {
replyRes = await this . doApiRequest ( reqMessageList ) ;
replyRes . message = await tw2s . convertPromise ( replyRes . message ) ;
if ( this . app . debug ) {
console . log ( replyRes ) ;
}
// 如果检测到对话中认为自己是AI, 则再次调用, 重写对话
if ( this . shouldSelfSuggestion ( replyRes . message ) ) {
if ( characterConf . self_suggestion_prompt && this . shouldSelfSuggestion ( replyRes . message ) ) {
this . app . logger . debug ( '需要重写回答' ) ;
replyRes = await this . chatComplete ( message . contentText , messageLogList , true ) ;
reqMessageList = this . buildMessageList ( replyRes . message , messageLogList , characterConf , true ) ;
replyRes = await this . doApiRequest ( reqMessageList ) ;
if ( this . app . debug ) {
console . log ( replyRes ) ;
}
replyRes . message = await tw2s . convertPromise ( replyRes . message ) ;
}
let content = replyRes . message . replace ( /\n\n/g , '\n' ) ;
for ( let [ inputText , replacement ] of Object . entries ( this . config . output_replace ) ) {
content = content . replace ( new RegExp ( inputText , 'g' ) , replacement ) ;
}
await message . sendReply ( content , true ) ;
}
if ( replyRes ) {
messageLogList . push ( {
role : 'user' ,
message : message.contentText ,
tokens : questionTokens ,
} , replyRes ) ;
await message . session . chat . set ( this . SESSION_KEY_API_CHAT_LOG , messageLogList , this . config . openai_api . memory_expire ) ;
await message . sendReply ( replyRes . message . replace ( /\n\n/g , '\n' ) , true ) ;
}
} catch ( err : any ) {
this . app . logger . error ( 'ChatGPT error' , err ) ;
console . error ( err ) ;
@ -493,10 +608,17 @@ export default class ChatGPTController implements PluginController {
await message . sendReply ( '提问太多了,过会儿再试试呗。' , true ) ;
return ;
}
} else if ( err . name === 'RequestError' ) {
await message . sendReply ( '连接失败:' + err . message + ',过会儿再试试呗。' , true ) ;
return ;
}
await message . sendReply ( '生成对话失败: ' + err . toString ( ) , true ) ;
return ;
} finally {
if ( singleMessage ) {
this . chatGenerating = false ;
}
}
}
}