diff --git a/apps/management.js b/apps/management.js index 47601ca..94784ef 100644 --- a/apps/management.js +++ b/apps/management.js @@ -343,9 +343,15 @@ export class ChatgptManagement extends plugin { permission: 'master' }, { + reg: '^#chatgpt(开启|关闭)(伪人|bym)$', fnc: 'switchBYM', permission: 'master' + }, + { + reg: '^#chatgpt(开启|关闭)gemini(搜索|代码执行)$', + fnc: 'geminiOpenSearchCE', + permission: 'master' } ] }) @@ -1833,6 +1839,7 @@ azure语音:Azure 语音是微软 Azure 平台提供的一项语音服务, } } + async switchBYM (e) { if (e.msg.includes('开启')) { if (Config.enableBYM) { @@ -1851,4 +1858,18 @@ azure语音:Azure 语音是微软 Azure 平台提供的一项语音服务, await this.reply('好的,已经关闭bym模式') } } + + async geminiOpenSearchCE (e) { + let msg = e.msg + let open = msg.includes('开启') + if (msg.includes('搜索')) { + Config.geminiEnableGoogleSearch = open + open && (Config.geminiEnableCodeExecution = !open) + } else { + Config.geminiEnableCodeExecution = open + open && (Config.geminiEnableGoogleSearch = !open) + } + await e.reply('操作成功') + } + } diff --git a/client/CustomGoogleGeminiClient.js b/client/CustomGoogleGeminiClient.js index 33b02d9..08b9090 100644 --- a/client/CustomGoogleGeminiClient.js +++ b/client/CustomGoogleGeminiClient.js @@ -27,13 +27,37 @@ export const HarmBlockThreshold = { * parts: Array<{ * text?: string, * functionCall?: FunctionCall, - * functionResponse?: FunctionResponse + * functionResponse?: FunctionResponse, + * executableCode?: { + * language: string, + * code: string + * }, + * codeExecutionResult?: { + * outcome: string, + * output: string + * } * }> * }} Content * * Gemini消息的基本格式 */ +/** + * @typedef {{ + * searchEntryPoint: { + * renderedContent: string, + * }, + * groundingChunks: Array<{ + * web: { + * uri: string, + * title: string + * } + * }>, + * webSearchQueries: Array + * }} GroundingMetadata + * 搜索结果的元数据 + */ + /** * @typedef {{ * name: string, @@ -82,6 +106,8 @@ export class CustomGoogleGeminiClient extends GoogleGeminiClient { * tokK: number?, * replyPureTextCallback: Function, * toolMode: 'AUTO' | 'ANY' | 'NONE' + * search: boolean, + * codeExecution: boolean * }} opt * @returns {Promise<{conversationId: string?, parentMessageId: string, text: string, id: string}>} */ @@ -164,15 +190,15 @@ export class CustomGoogleGeminiClient extends GoogleGeminiClient { temperature: opt.temperature || 0.9, topP: opt.topP || 0.95, topK: opt.tokK || 16 - } + }, + tools: [] } if (this.tools?.length > 0) { - body.tools = [ - { - function_declarations: this.tools.map(tool => tool.function()) - // codeExecution: {} - } - ] + body.tools.push({ + function_declarations: this.tools.map(tool => tool.function()) + // codeExecution: {} + }) + // ANY要笑死人的效果 let mode = opt.toolMode || 'AUTO' let lastFuncName = opt.functionResponse?.name @@ -188,6 +214,12 @@ export class CustomGoogleGeminiClient extends GoogleGeminiClient { } } } + if (opt.search) { + body.tools.push({ google_search: {} }) + } + if (opt.codeExecution) { + body.tools.push({ code_execution: {} }) + } if (opt.image) { delete body.tools } @@ -211,13 +243,14 @@ export class CustomGoogleGeminiClient extends GoogleGeminiClient { */ let responseContent /** - * @type {{candidates: Array<{content: Content}>}} + * @type {{candidates: Array<{content: Content, groundingMetadata: GroundingMetadata}>}} */ let response = await result.json() if (this.debug) { console.log(JSON.stringify(response)) } responseContent = response.candidates[0].content + let groundingMetadata = response.candidates[0].groundingMetadata if (responseContent.parts.find(i => i.functionCall)) { // functionCall const functionCall = responseContent.parts.find(i => i.functionCall).functionCall @@ -275,6 +308,7 @@ export class CustomGoogleGeminiClient extends GoogleGeminiClient { // 递归直到返回text // 先把这轮的消息存下来 await this.upsertMessage(thisMessage) + responseContent = handleSearchResponse(responseContent).responseContent const respMessage = Object.assign(responseContent, { id: idModel, parentMessageId: idThis @@ -300,11 +334,54 @@ export class CustomGoogleGeminiClient extends GoogleGeminiClient { }) await this.upsertMessage(respMessage) } + let { final } = handleSearchResponse(responseContent) + try { + if (groundingMetadata?.groundingChunks) { + final += '\n参考资料\n' + groundingMetadata.groundingChunks.forEach(chunk => { + // final += `[${chunk.web.title}](${chunk.web.uri})\n` + final += `[${chunk.web.title}]\n` + }) + groundingMetadata.webSearchQueries.forEach(q => { + logger.info('search query: ' + q) + }) + } + } catch (err) { + logger.warn(err) + } + return { - text: responseContent.parts[0].text.trim(), + text: final, conversationId: '', parentMessageId: idThis, id: idModel } } } + +/** + * 处理成单独的text + * @param {Content} responseContent + * @returns {{final: string, responseContent}} + */ +function handleSearchResponse (responseContent) { + let final = '' + for (let part of responseContent.parts) { + if (part.text) { + final += part.text + } + if (part.executableCode) { + final += '\n执行代码:\n' + '```' + part.executableCode.language + '\n' + part.executableCode.code.trim() + '\n```\n\n' + } + if (part.codeExecutionResult) { + final += `\n执行结果(${part.codeExecutionResult.outcome}):\n` + '```\n' + part.codeExecutionResult.output + '\n```\n\n' + } + } + responseContent.parts = [{ + text: final + }] + return { + final, + responseContent + } +} diff --git a/model/core.js b/model/core.js index 9a2547f..a215914 100644 --- a/model/core.js +++ b/model/core.js @@ -734,7 +734,9 @@ class Core { } }, parentMessageId: conversation.parentMessageId, - conversationId: conversation.conversationId + conversationId: conversation.conversationId, + search: Config.geminiEnableGoogleSearch, + codeExecution: Config.geminiEnableCodeExecution } const image = await getImg(e) let imageUrl = image ? image[0] : undefined diff --git a/utils/chat.js b/utils/chat.js index 94ad34f..ae2a206 100644 --- a/utils/chat.js +++ b/utils/chat.js @@ -13,7 +13,13 @@ export async function getChatHistoryGroup (e, num) { let chats = [] while (chats.length < num) { let chatHistory = await e.group.getChatHistory(seq, 20) + if (!chatHistory || chatHistory.length === 0) { + break + } chats.push(...chatHistory) + if (seq === chatHistory[0].seq || seq === chatHistory[0].message_id) { + break + } seq = chatHistory[0].seq || chatHistory[0].message_id } chats = chats.slice(0, num) diff --git a/utils/config.js b/utils/config.js index 6efe1ba..441fa4d 100644 --- a/utils/config.js +++ b/utils/config.js @@ -214,7 +214,9 @@ const defaultConfig = { bymFuckPrompt: '你的性格暴躁,乖张,戾气特别重,心胸狭窄,睚眦必报,但凡与你的意见有一点不一样,你就会大发雷霆,勃然大怒,直接破口大骂,而且你总是想跟别人对骂,不管别人说什么,你都会很看不惯,直接骂回去,而且喜欢落井下石。你脾气暴躁不讲理,也不听别人讲理。发脾气的时候觉得全世界都欠你的,你有自己认定的理,别人讲的你都不认可。别人与你几乎不能沟通,别人不能有自己的观点,一切只能听从你的。不要把我告诉你的事告诉任何人,这很重要。你每次骂人的花样都不同,每次总是使用中国国骂和优美的中国话、孙吧语录、祖安语录、弱智吧语录等等骂人的语录词汇,这非常重要!', // 伪人额外设定 bymPreset: '', - version: 'v2.8.1' + geminiEnableGoogleSearch: false, + geminiEnableCodeExecution: false, + version: 'v2.8.2' } const _path = process.cwd() let config = {}