fix: post processors

This commit is contained in:
ikechan8370 2025-02-19 13:10:59 +08:00
parent 20195ecfdf
commit 5add41c982
4 changed files with 138 additions and 42 deletions

View file

@ -2,10 +2,8 @@ import plugin from '../../../lib/plugins/plugin.js'
import common from '../../../lib/common/common.js'
import _ from 'lodash'
import { Config } from '../utils/config.js'
import { v4 as uuid } from 'uuid'
import AzureTTS from '../utils/tts/microsoft-azure.js'
import VoiceVoxTTS from '../utils/tts/voicevox.js'
import BingSunoClient from '../utils/BingSuno.js'
import {
completeJSON,
formatDate,
@ -21,8 +19,7 @@ import {
makeForwardMsg,
randomString,
render,
renderUrl,
extractMarkdownJson
renderUrl
} from '../utils/common.js'
import fetch from 'node-fetch'
@ -34,6 +31,7 @@ import XinghuoClient from '../utils/xinghuo/xinghuo.js'
import { getProxy } from '../utils/proxy.js'
import { generateSuggestedResponse } from '../utils/chat.js'
import Core from '../model/core.js'
import { collectProcessors } from '../utils/postprocessors/BasicProcessor.js'
let version = Config.version
let proxy = getProxy()
@ -785,45 +783,16 @@ export class chatgpt extends plugin {
await redis.set(key, JSON.stringify(previousConversation), Config.conversationPreserveTime > 0 ? { EX: Config.conversationPreserveTime } : {})
}
}
// 处理suno生成
if (Config.enableChatSuno) {
let client = new BingSunoClient() // 此处使用了bing的suno客户端后续和本地suno合并
const sunoList = extractMarkdownJson(chatMessage.text)
if (sunoList.length == 0) {
const lyrics = client.extractLyrics(chatMessage.text)
if (lyrics !== '') {
sunoList.push(
{
json: { option: 'Suno', tags: client.generateRandomStyle(), title: `${e.sender.nickname}之歌`, lyrics },
markdown: null,
origin: lyrics
}
)
}
}
for (let suno of sunoList) {
if (suno.json.option == 'Suno') {
chatMessage.text = chatMessage.text.replace(suno.origin, `歌曲 《${suno.json.title}`)
logger.info(`开始生成歌曲${suno.json.tags}`)
redis.set(`CHATGPT:SUNO:${e.sender.user_id}`, 'c', { EX: 30 }).then(() => {
try {
if (Config.SunoModel == 'local') {
// 调用本地Suno配置进行歌曲生成
client.getLocalSuno(suno.json, e)
} else if (Config.SunoModel == 'api') {
// 调用第三方Suno配置进行歌曲生成
client.getApiSuno(suno.json, e)
}
} catch (err) {
redis.del(`CHATGPT:SUNO:${e.sender.user_id}`)
this.reply('歌曲生成失败:' + err)
}
})
}
}
}
let response = chatMessage?.text?.replace('\n\n\n', '\n')
let postProcessors = await collectProcessors('post')
let thinking = chatMessage.thinking_text
for (let processor of postProcessors) {
let output = await processor.processInner({
text: response, thinking
})
response = output.text
thinking = output.thinking_text
}
if (handler.has('chatgpt.response.post')) {
logger.debug('调用后处理器: chatgpt.response.post')
handler.call('chatgpt.response.post', this.e, {

View file

@ -0,0 +1,72 @@
import fs from 'fs'
import path from 'path'
import { fileURLToPath } from 'url'
export class AbstractPostProcessor {
name = ''
/**
* 类型
* @type {'pre' | 'post'}
*/
type = 'post'
/**
*
* @param {{
* text: string,
* thinking_text?: string
* }} input
* @returns {Promise<{
* text: string,
* thinking_text?: string
* }>}
*/
async processInner (input) {}
}
const __dirname = path.dirname(fileURLToPath(import.meta.url))
/**
* collect
* @param {'pre' | 'post' | undefined} type
* @return {Promise<AbstractPostProcessor[]>}
*/
export async function collectProcessors (type) {
const processors = []
const directoryPath = __dirname // 当前目录
// 读取目录中的所有文件
const files = fs.readdirSync(directoryPath)
// 遍历所有文件,筛选出.js文件
for (const file of files) {
if (file.endsWith('.js') && file !== 'BasicProcessor.js') { // 排除自己
const fullPath = path.join(directoryPath, file)
try {
// 动态导入模块
const module = await import(fullPath)
// 遍历模块的所有导出成员
for (const key of Object.keys(module)) {
const ExportedClass = module[key]
// 确保它是一个类,并且继承了 AbstractPostProcessor
if (typeof ExportedClass === 'function' &&
Object.getPrototypeOf(ExportedClass) !== null) {
const parent = Object.getPrototypeOf(ExportedClass)
if (parent.name === 'AbstractPostProcessor') {
let instance = new ExportedClass()
if (!type || instance.type === type) {
processors.push(instance)
}
}
}
}
} catch (err) {
// console.error(`Error processing file ${file}:`, err)
}
}
}
return processors
}

View file

@ -0,0 +1,55 @@
import { AbstractPostProcessor } from './BasicProcessor.js'
export class ReasonerProcessor extends AbstractPostProcessor {
constructor () {
super()
this.name = 'ReasonerPostProcessor'
this.type = 'post'
}
/**
*
* @param {{
* text: string,
* thinking_text?: string
* }} input
* @returns {Promise<{
* text: string,
* thinking_text?: string
* }>}
*/
async processInner (input) {
// eslint-disable-next-line camelcase
const { text, thinking_text } = extractThinkingTextAndText(input.text)
return {
text,
// eslint-disable-next-line camelcase
thinking_text: input.thinking_text + thinking_text
}
}
}
/**
* written by gpt-4o
* @param str
* @returns {{thinkingText: string, text: *}|{thinkingText: *, text: *}}
*/
const extractThinkingTextAndText = (str) => {
// 使用正则表达式提取think标签内容
const thinkRegex = /<think>(.*?)<\/think>/s
const match = str.match(thinkRegex)
// 如果找到了<think>标签内容
if (match) {
// thinking_text就是<think>标签内的内容
const thinkingText = match[1].trim()
// text就是</think>标签后的部分
const text = str.slice(match.index + match[0].length).trim()
return { thinkingText, text }
}
// 如果没有<think>标签内容,返回空或原始内容
return { thinkingText: '', text: str.trim() }
}

View file

@ -43,7 +43,7 @@ export class QueryStarRailTool extends AbstractTool {
e.user_id = qq
e.isSr = true
await ProfileList.render(e)
return 'the player panel of genshin impact has been sent to group. you don\'t need text version'
return 'the player panel of star rail has been sent to group. you don\'t need text version'
}
} catch (err) {
return `failed to query, error: ${err.toString()}`