Merge branch 'v2' into v2

This commit is contained in:
ycxom 2025-02-15 20:28:44 +08:00 committed by GitHub
commit d8862d4469
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 58 additions and 114 deletions

View file

@ -99,7 +99,7 @@ export class CustomGoogleGeminiClient extends GoogleGeminiClient {
* parentMessageId: string?, * parentMessageId: string?,
* stream: boolean?, * stream: boolean?,
* onProgress: function?, * onProgress: function?,
* functionResponse: FunctionResponse?, * functionResponse?: FunctionResponse | FunctionResponse[],
* system: string?, * system: string?,
* image: string?, // 保留旧版单图片支持 * image: string?, // 保留旧版单图片支持
* images: string[], // 新增多图片支持 * images: string[], // 新增多图片支持
@ -110,7 +110,7 @@ export class CustomGoogleGeminiClient extends GoogleGeminiClient {
* replyPureTextCallback: Function, * replyPureTextCallback: Function,
* toolMode: 'AUTO' | 'ANY' | 'NONE' * toolMode: 'AUTO' | 'ANY' | 'NONE'
* search: boolean, * search: boolean,
* codeExecution: boolean * codeExecution: boolean,
* }} opt * }} opt
* @returns {Promise<{conversationId: string?, parentMessageId: string, text: string, id: string}>} * @returns {Promise<{conversationId: string?, parentMessageId: string, text: string, id: string}>}
*/ */
@ -139,15 +139,23 @@ export class CustomGoogleGeminiClient extends GoogleGeminiClient {
// } // }
const idThis = crypto.randomUUID() const idThis = crypto.randomUUID()
const idModel = crypto.randomUUID() const idModel = crypto.randomUUID()
const thisMessage = opt.functionResponse if (opt.functionResponse && !typeof Array.isArray(opt.functionResponse)) {
opt.functionResponse = [opt.functionResponse]
}
const thisMessage = opt.functionResponse?.length > 0
? { ? {
role: 'user', role: 'user',
parts: [{ // parts: [{
functionResponse: opt.functionResponse // functionResponse: opt.functionResponse
}], // }],
id: idThis, parts: opt.functionResponse.map(i => {
parentMessageId: opt.parentMessageId || undefined return {
} functionResponse: i
}
}),
id: idThis,
parentMessageId: opt.parentMessageId || undefined
}
: { : {
role: 'user', role: 'user',
parts: text ? [{ text }] : [], parts: text ? [{ text }] : [],
@ -257,7 +265,9 @@ export class CustomGoogleGeminiClient extends GoogleGeminiClient {
delete content.parentMessageId delete content.parentMessageId
delete content.conversationId delete content.conversationId
}) })
// logger.info(JSON.stringify(body)) if (this.debug) {
logger.info(JSON.stringify(body))
}
let result = await newFetch(url, { let result = await newFetch(url, {
method: 'POST', method: 'POST',
body: JSON.stringify(body), body: JSON.stringify(body),
@ -281,19 +291,19 @@ export class CustomGoogleGeminiClient extends GoogleGeminiClient {
} }
responseContent = response.candidates[0].content responseContent = response.candidates[0].content
let groundingMetadata = response.candidates[0].groundingMetadata let groundingMetadata = response.candidates[0].groundingMetadata
if (responseContent.parts.find(i => i.functionCall)) { if (responseContent.parts.filter(i => i.functionCall).length > 0) {
// functionCall // functionCall
const functionCall = responseContent.parts.find(i => i.functionCall).functionCall const functionCall = responseContent.parts.filter(i => i.functionCall).map(i => i.functionCall)
const text = responseContent.parts.find(i => i.text)?.text const text = responseContent.parts.find(i => i.text)?.text
if (text) { if (text) {
// send reply first // send reply first
logger.info('send message: ' + text) logger.info('send message: ' + text)
opt.replyPureTextCallback && await opt.replyPureTextCallback(text) opt.replyPureTextCallback && await opt.replyPureTextCallback(text)
} }
// Gemini有时候只回复一个空的functionCall,无语死了 let /** @type {FunctionResponse[]} **/ fcResults = []
if (functionCall.name) { for (let fc of functionCall) {
logger.info(JSON.stringify(functionCall)) logger.info(JSON.stringify(fc))
const funcName = functionCall.name const funcName = fc.name
let chosenTool = this.tools.find(t => t.name === funcName) let chosenTool = this.tools.find(t => t.name === funcName)
/** /**
* @type {FunctionResponse} * @type {FunctionResponse}
@ -315,7 +325,7 @@ export class CustomGoogleGeminiClient extends GoogleGeminiClient {
try { try {
let isAdmin = ['admin', 'owner'].includes(this.e.sender.role) || (this.e.group?.is_admin && this.e.isMaster) let isAdmin = ['admin', 'owner'].includes(this.e.sender.role) || (this.e.group?.is_admin && this.e.isMaster)
let isOwner = ['owner'].includes(this.e.sender.role) || (this.e.group?.is_owner && this.e.isMaster) let isOwner = ['owner'].includes(this.e.sender.role) || (this.e.group?.is_owner && this.e.isMaster)
let args = Object.assign(functionCall.args, { let args = Object.assign(fc.args, {
isAdmin, isAdmin,
isOwner, isOwner,
sender: this.e.sender, sender: this.e.sender,
@ -332,29 +342,21 @@ export class CustomGoogleGeminiClient extends GoogleGeminiClient {
} }
} }
} }
let responseOpt = _.cloneDeep(opt) fcResults.push(functionResponse)
responseOpt.parentMessageId = idModel
responseOpt.functionResponse = functionResponse
// 递归直到返回text
// 先把这轮的消息存下来
await this.upsertMessage(thisMessage)
responseContent = handleSearchResponse(responseContent).responseContent
const respMessage = Object.assign(responseContent, {
id: idModel,
parentMessageId: idThis
})
await this.upsertMessage(respMessage)
return await this.sendMessage('', responseOpt)
} else {
// 谷歌抽风了,瞎调函数,不保存这轮,直接返回
return {
text: '',
conversationId: '',
parentMessageId: opt.parentMessageId,
id: '',
error: true
}
} }
let responseOpt = _.cloneDeep(opt)
responseOpt.parentMessageId = idModel
responseOpt.functionResponse = fcResults
// 递归直到返回text
// 先把这轮的消息存下来
await this.upsertMessage(thisMessage)
responseContent = handleSearchResponse(responseContent).responseContent
const respMessage = Object.assign(responseContent, {
id: idModel,
parentMessageId: idThis
})
await this.upsertMessage(respMessage)
return await this.sendMessage('', responseOpt)
} }
if (responseContent) { if (responseContent) {
await this.upsertMessage(thisMessage) await this.upsertMessage(thisMessage)

View file

@ -790,14 +790,14 @@ async function collectTools (e) {
} }
let fullTools = [ let fullTools = [
new EditCardTool(), new EditCardTool(),
new QueryStarRailTool(), // new QueryStarRailTool(),
new WebsiteTool(), new WebsiteTool(),
new JinyanTool(), new JinyanTool(),
new KickOutTool(), new KickOutTool(),
new WeatherTool(), new WeatherTool(),
new SendPictureTool(), new SendPictureTool(),
new SendVideoTool(), new SendVideoTool(),
new ImageCaptionTool(), // new ImageCaptionTool(),
new SearchVideoTool(), new SearchVideoTool(),
new SendAvatarTool(), new SendAvatarTool(),
new SerpImageTool(), new SerpImageTool(),
@ -805,45 +805,40 @@ async function collectTools (e) {
new SendMusicTool(), new SendMusicTool(),
new SerpIkechan8370Tool(), new SerpIkechan8370Tool(),
new SerpTool(), new SerpTool(),
new SendAudioMessageTool(), // new SendAudioMessageTool(),
new ProcessPictureTool(), // new ProcessPictureTool(),
new APTool(), new APTool(),
new HandleMessageMsgTool(), new HandleMessageMsgTool(),
new QueryUserinfoTool(), new QueryUserinfoTool(),
new EliMusicTool(), // new EliMusicTool(),
new EliMovieTool(), // new EliMovieTool(),
new SendMessageToSpecificGroupOrUserTool(), new SendMessageToSpecificGroupOrUserTool(),
new SendDiceTool(), new SendDiceTool(),
new QueryGenshinTool(), new QueryGenshinTool(),
new SetTitleTool() new SetTitleTool()
] ]
// todo 3.0再重构tool的插拔和管理 // todo 3.0再重构tool的插拔和管理
let tools = [ let /** @type{AbstractTool} **/ tools = [
new SendAvatarTool(), new SendAvatarTool(),
new SendDiceTool(), new SendDiceTool(),
new SendMessageToSpecificGroupOrUserTool(), new SendMessageToSpecificGroupOrUserTool(),
// new EditCardTool(), // new EditCardTool(),
new QueryStarRailTool(), // new QueryStarRailTool(),
new QueryGenshinTool(), new QueryGenshinTool(),
new SendMusicTool(),
new SearchMusicTool(),
new ProcessPictureTool(), new ProcessPictureTool(),
new WebsiteTool(), new WebsiteTool(),
// new JinyanTool(), // new JinyanTool(),
// new KickOutTool(), // new KickOutTool(),
new WeatherTool(), new WeatherTool(),
new SendPictureTool(), new SendPictureTool(),
new SendAudioMessageTool(), // new SendAudioMessageTool(),
new APTool(), new APTool(),
// new HandleMessageMsgTool(), // new HandleMessageMsgTool(),
serpTool, serpTool,
new QueryUserinfoTool() new QueryUserinfoTool()
] ]
try {
await import('../../avocado-plugin/apps/avocado.js')
tools.push(...[new EliMusicTool(), new EliMovieTool()])
} catch (err) {
tools.push(...[new SendMusicTool(), new SearchMusicTool()])
// logger.debug(logger.green('【ChatGPT-Plugin】插件avocado-plugin未安装') + ',安装后可查看最近热映电影与体验可玩性更高的点歌工具。\n可前往 https://github.com/Qz-Sean/avocado-plugin 获取')
}
let systemAddition = '' let systemAddition = ''
if (e.isGroup) { if (e.isGroup) {
let botInfo = await e.bot?.pickMember?.(e.group_id, getUin(e), true) || await e.bot?.getGroupMemberInfo?.(e.group_id, getUin(e), true) let botInfo = await e.bot?.pickMember?.(e.group_id, getUin(e), true) || await e.bot?.getGroupMemberInfo?.(e.group_id, getUin(e), true)
@ -862,8 +857,8 @@ async function collectTools (e) {
let promptAddition = '' let promptAddition = ''
let img = await getImg(e) let img = await getImg(e)
if (img?.length > 0 && Config.extraUrl) { if (img?.length > 0 && Config.extraUrl) {
tools.push(new ImageCaptionTool()) // tools.push(new ImageCaptionTool())
tools.push(new ProcessPictureTool()) // tools.push(new ProcessPictureTool())
promptAddition += `\nthe url of the picture(s) above: ${img.join(', ')}` promptAddition += `\nthe url of the picture(s) above: ${img.join(', ')}`
} else { } else {
tools.push(new SerpImageTool()) tools.push(new SerpImageTool())
@ -892,57 +887,4 @@ async function collectTools (e) {
} }
} }
async function getAvailableBingToken (conversation, throttled = []) {
let allThrottled = false
if (!await redis.get('CHATGPT:BING_TOKENS')) {
return {
bingToken: null,
allThrottled
}
// throw new Error('未绑定Bing Cookie请使用#chatgpt设置必应token命令绑定Bing Cookie')
}
let bingToken = ''
let bingTokens = JSON.parse(await redis.get('CHATGPT:BING_TOKENS'))
const normal = bingTokens.filter(element => element.State === '正常')
const restricted = bingTokens.filter(element => element.State === '受限')
// 判断受限的token是否已经可以解除
for (const restrictedToken of restricted) {
const now = new Date()
const tk = new Date(restrictedToken.DisactivationTime)
if (tk <= now) {
const index = bingTokens.findIndex(element => element.Token === restrictedToken.Token)
bingTokens[index].Usage = 0
bingTokens[index].State = '正常'
}
}
if (normal.length > 0) {
const minElement = normal.reduce((min, current) => {
return current.Usage < min.Usage ? current : min
})
bingToken = minElement.Token
} else if (restricted.length > 0 && restricted.some(x => throttled.includes(x.Token))) {
allThrottled = true
const minElement = restricted.reduce((min, current) => {
return current.Usage < min.Usage ? current : min
})
bingToken = minElement.Token
} else {
// throw new Error('全部Token均已失效暂时无法使用')
return {
bingToken: null,
allThrottled
}
}
// 记录使用情况
const index = bingTokens.findIndex(element => element.Token === bingToken)
bingTokens[index].Usage += 1
await redis.set('CHATGPT:BING_TOKENS', JSON.stringify(bingTokens))
return {
bingToken,
allThrottled
}
}
export default new Core() export default new Core()

View file

@ -53,7 +53,7 @@ export class SendPictureTool extends AbstractTool {
} }
} }
// await group.sendMsg(pictures) // await group.sendMsg(pictures)
return 'picture has been sent to group' + target + errs.length > 0 ? `, but some pictures failed to send (${errs.join('、')})` : '' return 'picture has been sent to group' + target + (errs.length > 0 ? `, but some pictures failed to send (${errs.join('、')})` : '')
} else { } else {
let user = e.bot.pickUser(target) let user = e.bot.pickUser(target)
if (e.group_id) { if (e.group_id) {
@ -61,12 +61,12 @@ export class SendPictureTool extends AbstractTool {
} }
for (let pic of pictures) { for (let pic of pictures) {
try { try {
await user.sendMsg(pictures) await user.sendMsg(pic)
} catch (err) { } catch (err) {
errs.push(pic.url) errs.push(pic.url)
} }
} }
return 'picture has been sent to user' + target + errs.length > 0 ? `, but some pictures failed to send (${errs.join('、')})` : '' return 'picture has been sent to user' + target + (errs.length > 0 ? `, but some pictures failed to send (${errs.join('、')})` : '')
} }
} catch (err) { } catch (err) {
return `failed to send pictures, error: ${JSON.stringify(err)}` return `failed to send pictures, error: ${JSON.stringify(err)}`