diff --git a/client/CustomGoogleGeminiClient.js b/client/CustomGoogleGeminiClient.js index b59384f..57fd51c 100644 --- a/client/CustomGoogleGeminiClient.js +++ b/client/CustomGoogleGeminiClient.js @@ -99,7 +99,7 @@ export class CustomGoogleGeminiClient extends GoogleGeminiClient { * parentMessageId: string?, * stream: boolean?, * onProgress: function?, - * functionResponse: FunctionResponse?, + * functionResponse?: FunctionResponse | FunctionResponse[], * system: string?, * image: string?, // 保留旧版单图片支持 * images: string[], // 新增多图片支持 @@ -110,7 +110,7 @@ export class CustomGoogleGeminiClient extends GoogleGeminiClient { * replyPureTextCallback: Function, * toolMode: 'AUTO' | 'ANY' | 'NONE' * search: boolean, - * codeExecution: boolean + * codeExecution: boolean, * }} opt * @returns {Promise<{conversationId: string?, parentMessageId: string, text: string, id: string}>} */ @@ -139,15 +139,23 @@ export class CustomGoogleGeminiClient extends GoogleGeminiClient { // } const idThis = 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', - parts: [{ - functionResponse: opt.functionResponse - }], - id: idThis, - parentMessageId: opt.parentMessageId || undefined - } + role: 'user', + // parts: [{ + // functionResponse: opt.functionResponse + // }], + parts: opt.functionResponse.map(i => { + return { + functionResponse: i + } + }), + id: idThis, + parentMessageId: opt.parentMessageId || undefined + } : { role: 'user', parts: text ? [{ text }] : [], @@ -257,7 +265,9 @@ export class CustomGoogleGeminiClient extends GoogleGeminiClient { delete content.parentMessageId delete content.conversationId }) - // logger.info(JSON.stringify(body)) + if (this.debug) { + logger.info(JSON.stringify(body)) + } let result = await newFetch(url, { method: 'POST', body: JSON.stringify(body), @@ -281,19 +291,19 @@ export class CustomGoogleGeminiClient extends GoogleGeminiClient { } responseContent = response.candidates[0].content let groundingMetadata = response.candidates[0].groundingMetadata - if (responseContent.parts.find(i => i.functionCall)) { + if (responseContent.parts.filter(i => i.functionCall).length > 0) { // 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 if (text) { // send reply first logger.info('send message: ' + text) opt.replyPureTextCallback && await opt.replyPureTextCallback(text) } - // Gemini有时候只回复一个空的functionCall,无语死了 - if (functionCall.name) { - logger.info(JSON.stringify(functionCall)) - const funcName = functionCall.name + let /** @type {FunctionResponse[]} **/ fcResults = [] + for (let fc of functionCall) { + logger.info(JSON.stringify(fc)) + const funcName = fc.name let chosenTool = this.tools.find(t => t.name === funcName) /** * @type {FunctionResponse} @@ -315,7 +325,7 @@ export class CustomGoogleGeminiClient extends GoogleGeminiClient { try { 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 args = Object.assign(functionCall.args, { + let args = Object.assign(fc.args, { isAdmin, isOwner, sender: this.e.sender, @@ -332,29 +342,21 @@ export class CustomGoogleGeminiClient extends GoogleGeminiClient { } } } - let responseOpt = _.cloneDeep(opt) - 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 - } + fcResults.push(functionResponse) } + 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) { await this.upsertMessage(thisMessage) diff --git a/model/core.js b/model/core.js index e8af1a4..bfe7915 100644 --- a/model/core.js +++ b/model/core.js @@ -790,14 +790,14 @@ async function collectTools (e) { } let fullTools = [ new EditCardTool(), - new QueryStarRailTool(), + // new QueryStarRailTool(), new WebsiteTool(), new JinyanTool(), new KickOutTool(), new WeatherTool(), new SendPictureTool(), new SendVideoTool(), - new ImageCaptionTool(), + // new ImageCaptionTool(), new SearchVideoTool(), new SendAvatarTool(), new SerpImageTool(), @@ -805,45 +805,40 @@ async function collectTools (e) { new SendMusicTool(), new SerpIkechan8370Tool(), new SerpTool(), - new SendAudioMessageTool(), - new ProcessPictureTool(), + // new SendAudioMessageTool(), + // new ProcessPictureTool(), new APTool(), new HandleMessageMsgTool(), new QueryUserinfoTool(), - new EliMusicTool(), - new EliMovieTool(), + // new EliMusicTool(), + // new EliMovieTool(), new SendMessageToSpecificGroupOrUserTool(), new SendDiceTool(), new QueryGenshinTool(), new SetTitleTool() ] // todo 3.0再重构tool的插拔和管理 - let tools = [ + let /** @type{AbstractTool} **/ tools = [ new SendAvatarTool(), new SendDiceTool(), new SendMessageToSpecificGroupOrUserTool(), // new EditCardTool(), - new QueryStarRailTool(), + // new QueryStarRailTool(), new QueryGenshinTool(), + new SendMusicTool(), + new SearchMusicTool(), new ProcessPictureTool(), new WebsiteTool(), // new JinyanTool(), // new KickOutTool(), new WeatherTool(), new SendPictureTool(), - new SendAudioMessageTool(), + // new SendAudioMessageTool(), new APTool(), // new HandleMessageMsgTool(), serpTool, 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 = '' 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) @@ -862,8 +857,8 @@ async function collectTools (e) { let promptAddition = '' let img = await getImg(e) if (img?.length > 0 && Config.extraUrl) { - tools.push(new ImageCaptionTool()) - tools.push(new ProcessPictureTool()) + // tools.push(new ImageCaptionTool()) + // tools.push(new ProcessPictureTool()) promptAddition += `\nthe url of the picture(s) above: ${img.join(', ')}` } else { 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() diff --git a/utils/tools/SendPictureTool.js b/utils/tools/SendPictureTool.js index b521caf..5a436ab 100644 --- a/utils/tools/SendPictureTool.js +++ b/utils/tools/SendPictureTool.js @@ -53,7 +53,7 @@ export class SendPictureTool extends AbstractTool { } } // 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 { let user = e.bot.pickUser(target) if (e.group_id) { @@ -61,12 +61,12 @@ export class SendPictureTool extends AbstractTool { } for (let pic of pictures) { try { - await user.sendMsg(pictures) + await user.sendMsg(pic) } catch (err) { 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) { return `failed to send pictures, error: ${JSON.stringify(err)}`