From 0ad0e2d23754e7d4a62db5e55ed5c6b61d403354 Mon Sep 17 00:00:00 2001 From: ikechan8370 Date: Mon, 17 Feb 2025 12:41:51 +0800 Subject: [PATCH 1/7] feat: add github tool --- model/core.js | 7 +++-- utils/config.js | 2 ++ utils/tools/GithubTool.js | 59 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 utils/tools/GithubTool.js diff --git a/model/core.js b/model/core.js index be8306a..eb1415b 100644 --- a/model/core.js +++ b/model/core.js @@ -54,6 +54,7 @@ import { QwenApi } from '../utils/alibaba/qwen-api.js' import { BingAIClient } from '../client/CopilotAIClient.js' import Keyv from 'keyv' import crypto from 'crypto' +import {GithubAPITool} from '../utils/tools/GithubTool.js' export const roleMap = { owner: 'group owner', @@ -774,7 +775,8 @@ async function collectTools (e) { new SendMessageToSpecificGroupOrUserTool(), new SendDiceTool(), new QueryGenshinTool(), - new SetTitleTool() + new SetTitleTool(), + new GithubAPITool() ] // todo 3.0再重构tool的插拔和管理 let /** @type{AbstractTool[]} **/ tools = [ @@ -796,7 +798,8 @@ async function collectTools (e) { new APTool(), // new HandleMessageMsgTool(), serpTool, - new QueryUserinfoTool() + new QueryUserinfoTool(), + new GithubAPITool() ] let systemAddition = '' if (e.isGroup) { diff --git a/utils/config.js b/utils/config.js index edba6a4..4c9a9eb 100644 --- a/utils/config.js +++ b/utils/config.js @@ -229,6 +229,8 @@ const defaultConfig = { apiMaxToken: 4096, enableToolPrivateSend: true, // 是否允许智能模式下私聊骚扰其他群友。主人不受影响。 geminiForceToolKeywords: [], + githubAPI: 'https://api.github.com', + githubAPIKey: '', version: 'v2.8.4' } const _path = process.cwd() diff --git a/utils/tools/GithubTool.js b/utils/tools/GithubTool.js new file mode 100644 index 0000000..b110b8c --- /dev/null +++ b/utils/tools/GithubTool.js @@ -0,0 +1,59 @@ +import { AbstractTool } from './AbstractTool.js' +import { Config } from '../config.js' + +export class GithubAPITool extends AbstractTool { + name = 'github' + + parameters = { + properties: { + q: { + type: 'string', + description: 'search keyword' + }, + type: { + type: 'string', + enum: ['repositories', 'issues', 'users', 'code', 'custom'], + description: 'search type. If custom is chosen, you must provide full github api url path.' + }, + num: { + type: 'number', + description: 'search results limit number, default is 5' + }, + fullUrl: { + type: 'string', + description: 'if type is custom, you need provide this, such as /repos/OWNER/REPO/actions/artifacts?name=NAME&page=2&per_page=1' + } + }, + required: ['q', 'type'] + } + + func = async function (opts) { + let { q, type, num = 5, fullUrl = '' } = opts + let headers = { + 'X-From-Library': 'ikechan8370', + Accept: 'application/vnd.github+json' + } + if (Config.githubAPIKey) { + headers.Authorization = `Bearer ${Config.githubAPIKey}` + } + let res + if (type !== 'custom') { + let serpRes = await fetch(`${Config.githubAPI}/search/${type}?q=${encodeURIComponent(q)}&per_page=${num}`, { + headers + }) + serpRes = await serpRes.json() + + res = serpRes.items + } else { + let serpRes = await fetch(`${Config.githubAPI}${fullUrl}`, { + headers + }) + serpRes = await serpRes.json() + res = serpRes.items + } + + return `the search results are here in json format:\n${JSON.stringify(res)} \n(Notice that these information are only available for you, the user cannot see them, you next answer should consider about the information)` + } + + description = 'Useful when you want to search something from api.github.com. You can use preset search types or build your own url path with order, per_page, page and other params.' +} From 41be6befec14a7a161dd8f716b6958b99c011245 Mon Sep 17 00:00:00 2001 From: ikechan8370 Date: Mon, 17 Feb 2025 13:24:30 +0800 Subject: [PATCH 2/7] fix: github tool adjustment --- utils/tools/GithubTool.js | 10 +++++----- utils/tools/WebsiteTool.js | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/utils/tools/GithubTool.js b/utils/tools/GithubTool.js index b110b8c..50fd8f4 100644 --- a/utils/tools/GithubTool.js +++ b/utils/tools/GithubTool.js @@ -8,7 +8,7 @@ export class GithubAPITool extends AbstractTool { properties: { q: { type: 'string', - description: 'search keyword' + description: 'search keyword. you should build it. If you want to find from specified repo, please must use repo:ORG/REPO as part of the keyword. For example, if you want to find the oldest unresolved Python bugs on Windows. Your query might look something like this: q=windows+label:bug+language:python+state:open&sort=created&order=asc' }, type: { type: 'string', @@ -21,7 +21,7 @@ export class GithubAPITool extends AbstractTool { }, fullUrl: { type: 'string', - description: 'if type is custom, you need provide this, such as /repos/OWNER/REPO/actions/artifacts?name=NAME&page=2&per_page=1' + description: 'if type is custom, you need provide this, such as /repos/OWNER/REPO/actions/artifacts?name=NAME&page=2&per_page=1. if type is not custom, is will be ignored' } }, required: ['q', 'type'] @@ -43,17 +43,17 @@ export class GithubAPITool extends AbstractTool { }) serpRes = await serpRes.json() - res = serpRes.items + res = serpRes } else { let serpRes = await fetch(`${Config.githubAPI}${fullUrl}`, { headers }) serpRes = await serpRes.json() - res = serpRes.items + res = serpRes } return `the search results are here in json format:\n${JSON.stringify(res)} \n(Notice that these information are only available for you, the user cannot see them, you next answer should consider about the information)` } - description = 'Useful when you want to search something from api.github.com. You can use preset search types or build your own url path with order, per_page, page and other params.' + description = 'Useful when you want to search something from api.github.com. You can use preset search types or build your own url path with order, per_page, page and other params. Automatically adjust the query and params if any error messages return.' } diff --git a/utils/tools/WebsiteTool.js b/utils/tools/WebsiteTool.js index 88b383c..34a11b9 100644 --- a/utils/tools/WebsiteTool.js +++ b/utils/tools/WebsiteTool.js @@ -115,5 +115,5 @@ export class WebsiteTool extends AbstractTool { } } - description = 'Useful when you want to browse a website by url' + description = 'Useful when you want to browse a website by url, it can be a html or api url' } From 0fae49d5d16cf1d69f6fe42fadc6343b54c12f7f Mon Sep 17 00:00:00 2001 From: ikechan8370 Date: Mon, 17 Feb 2025 22:45:39 +0800 Subject: [PATCH 3/7] fix: adjust website tools; trim gemini intermediate response --- client/CustomGoogleGeminiClient.js | 6 +- utils/tools/WebsiteTool.js | 111 ++++++++++++++--------------- 2 files changed, 56 insertions(+), 61 deletions(-) diff --git a/client/CustomGoogleGeminiClient.js b/client/CustomGoogleGeminiClient.js index 86ddd85..efe76bf 100644 --- a/client/CustomGoogleGeminiClient.js +++ b/client/CustomGoogleGeminiClient.js @@ -281,10 +281,10 @@ export class CustomGoogleGeminiClient extends GoogleGeminiClient { // functionCall const functionCall = responseContent.parts.filter(i => i.functionCall).map(i => i.functionCall) const text = responseContent.parts.find(i => i.text)?.text - if (text) { + if (text && text.trim()) { // send reply first - logger.info('send message: ' + text) - opt.replyPureTextCallback && await opt.replyPureTextCallback(text) + logger.info('send message: ' + text.trim()) + opt.replyPureTextCallback && await opt.replyPureTextCallback(text.trim()) } let /** @type {FunctionResponse[]} **/ fcResults = [] for (let fc of functionCall) { diff --git a/utils/tools/WebsiteTool.js b/utils/tools/WebsiteTool.js index 34a11b9..0f509d6 100644 --- a/utils/tools/WebsiteTool.js +++ b/utils/tools/WebsiteTool.js @@ -6,6 +6,58 @@ import proxy from 'https-proxy-agent' import { getMaxModelTokens } from '../common.js' import { ChatGPTPuppeteer } from '../browser.js' import { CustomGoogleGeminiClient } from '../../client/CustomGoogleGeminiClient.js' + +/** + * Generated by GPT-4o + * @param html + * @returns {*} + */ +function cleanHTML (html) { + // 1. 移除