mirror of
https://github.com/ikechan8370/chatgpt-plugin.git
synced 2025-12-16 13:27:08 +00:00
feat: 支持GeminiPro模型
This commit is contained in:
parent
ac0aa7d02d
commit
220525fbbd
11 changed files with 415 additions and 218 deletions
|
|
@ -42,6 +42,9 @@
|
||||||
* 2023-09-10 支持来自claude.ai的claude-2模型
|
* 2023-09-10 支持来自claude.ai的claude-2模型
|
||||||
* 2023-10-19 支持读取文件,(目前适配必应模式和Claude2模式)
|
* 2023-10-19 支持读取文件,(目前适配必应模式和Claude2模式)
|
||||||
* 2023-10-25 增加支持通义千问官方API
|
* 2023-10-25 增加支持通义千问官方API
|
||||||
|
* 2023-12-01 持续优先适配Shamrock
|
||||||
|
* 2023-12-14 增加支持Gemini 官方API
|
||||||
|
|
||||||
### 如果觉得这个插件有趣或者对你有帮助,请点一个star吧!
|
### 如果觉得这个插件有趣或者对你有帮助,请点一个star吧!
|
||||||
|
|
||||||
## 版本要求
|
## 版本要求
|
||||||
|
|
|
||||||
234
apps/chat.js
234
apps/chat.js
|
|
@ -4,7 +4,6 @@ import { Config, defaultOpenAIAPI } from '../utils/config.js'
|
||||||
import { v4 as uuid } from 'uuid'
|
import { v4 as uuid } from 'uuid'
|
||||||
import delay from 'delay'
|
import delay from 'delay'
|
||||||
import { ChatGPTAPI } from '../utils/openai/chatgpt-api.js'
|
import { ChatGPTAPI } from '../utils/openai/chatgpt-api.js'
|
||||||
import { BingAIClient } from '@waylaidwanderer/chatgpt-api'
|
|
||||||
import SydneyAIClient from '../utils/SydneyAIClient.js'
|
import SydneyAIClient from '../utils/SydneyAIClient.js'
|
||||||
import { PoeClient } from '../utils/poe/index.js'
|
import { PoeClient } from '../utils/poe/index.js'
|
||||||
import AzureTTS from '../utils/tts/microsoft-azure.js'
|
import AzureTTS from '../utils/tts/microsoft-azure.js'
|
||||||
|
|
@ -78,6 +77,7 @@ import { ClaudeAIClient } from '../utils/claude.ai/index.js'
|
||||||
import { getProxy } from '../utils/proxy.js'
|
import { getProxy } from '../utils/proxy.js'
|
||||||
import { QwenApi } from '../utils/alibaba/qwen-api.js'
|
import { QwenApi } from '../utils/alibaba/qwen-api.js'
|
||||||
import { getChatHistoryGroup } from '../utils/chat.js'
|
import { getChatHistoryGroup } from '../utils/chat.js'
|
||||||
|
import { GoogleGeminiClient } from '../client/GoogleGeminiClient.js'
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await import('@azure/openai')
|
await import('@azure/openai')
|
||||||
|
|
@ -194,6 +194,12 @@ export class chatgpt extends plugin {
|
||||||
/** 执行方法 */
|
/** 执行方法 */
|
||||||
fnc: 'qwen'
|
fnc: 'qwen'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
/** 命令正则匹配 */
|
||||||
|
reg: '^#gemini[sS]*',
|
||||||
|
/** 执行方法 */
|
||||||
|
fnc: 'gemini'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
/** 命令正则匹配 */
|
/** 命令正则匹配 */
|
||||||
reg: toggleMode === 'at' ? '^[^#][sS]*' : '^#chat[^gpt][sS]*',
|
reg: toggleMode === 'at' ? '^[^#][sS]*' : '^#chat[^gpt][sS]*',
|
||||||
|
|
@ -397,6 +403,14 @@ export class chatgpt extends plugin {
|
||||||
await redis.del(`CHATGPT:CONVERSATIONS_QWEN:${e.sender.user_id}`)
|
await redis.del(`CHATGPT:CONVERSATIONS_QWEN:${e.sender.user_id}`)
|
||||||
await this.reply('已结束当前对话,请@我进行聊天以开启新的对话', true)
|
await this.reply('已结束当前对话,请@我进行聊天以开启新的对话', true)
|
||||||
}
|
}
|
||||||
|
} else if (use === 'gemini') {
|
||||||
|
let c = await redis.get(`CHATGPT:CONVERSATIONS_GEMINI:${e.sender.user_id}`)
|
||||||
|
if (!c) {
|
||||||
|
await this.reply('当前没有开启对话', true)
|
||||||
|
} else {
|
||||||
|
await redis.del(`CHATGPT:CONVERSATIONS_GEMINI:${e.sender.user_id}`)
|
||||||
|
await this.reply('已结束当前对话,请@我进行聊天以开启新的对话', true)
|
||||||
|
}
|
||||||
} else if (use === 'bing') {
|
} else if (use === 'bing') {
|
||||||
let c = await redis.get(`CHATGPT:CONVERSATIONS_BING:${e.sender.user_id}`)
|
let c = await redis.get(`CHATGPT:CONVERSATIONS_BING:${e.sender.user_id}`)
|
||||||
if (!c) {
|
if (!c) {
|
||||||
|
|
@ -466,6 +480,14 @@ export class chatgpt extends plugin {
|
||||||
await redis.del(`CHATGPT:CONVERSATIONS_QWEN:${qq}`)
|
await redis.del(`CHATGPT:CONVERSATIONS_QWEN:${qq}`)
|
||||||
await this.reply(`已结束${atUser}的对话,TA仍可以@我进行聊天以开启新的对话`, true)
|
await this.reply(`已结束${atUser}的对话,TA仍可以@我进行聊天以开启新的对话`, true)
|
||||||
}
|
}
|
||||||
|
} else if (use === 'gemini') {
|
||||||
|
let c = await redis.get(`CHATGPT:CONVERSATIONS_GEMINI:${qq}`)
|
||||||
|
if (!c) {
|
||||||
|
await this.reply(`当前${atUser}没有开启对话`, true)
|
||||||
|
} else {
|
||||||
|
await redis.del(`CHATGPT:CONVERSATIONS_GEMINI:${qq}`)
|
||||||
|
await this.reply(`已结束${atUser}的对话,TA仍可以@我进行聊天以开启新的对话`, true)
|
||||||
|
}
|
||||||
} else if (use === 'bing') {
|
} else if (use === 'bing') {
|
||||||
let c = await redis.get(`CHATGPT:CONVERSATIONS_BING:${qq}`)
|
let c = await redis.get(`CHATGPT:CONVERSATIONS_BING:${qq}`)
|
||||||
if (!c) {
|
if (!c) {
|
||||||
|
|
@ -597,6 +619,18 @@ export class chatgpt extends plugin {
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
case 'gemini': {
|
||||||
|
let qcs = await redis.keys('CHATGPT:CONVERSATIONS_GEMINI:*')
|
||||||
|
for (let i = 0; i < qcs.length; i++) {
|
||||||
|
await redis.del(qcs[i])
|
||||||
|
// todo clean last message id
|
||||||
|
if (Config.debug) {
|
||||||
|
logger.info('delete gemini conversation bind: ' + qcs[i])
|
||||||
|
}
|
||||||
|
deleted++
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
await this.reply(`结束了${deleted}个用户的对话。`, true)
|
await this.reply(`结束了${deleted}个用户的对话。`, true)
|
||||||
}
|
}
|
||||||
|
|
@ -1092,6 +1126,10 @@ export class chatgpt extends plugin {
|
||||||
key = `CHATGPT:CONVERSATIONS_QWEN:${(e.isGroup && Config.groupMerge) ? e.group_id.toString() : e.sender.user_id}`
|
key = `CHATGPT:CONVERSATIONS_QWEN:${(e.isGroup && Config.groupMerge) ? e.group_id.toString() : e.sender.user_id}`
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
case 'gemini': {
|
||||||
|
key = `CHATGPT:CONVERSATIONS_GEMINI:${(e.isGroup && Config.groupMerge) ? e.group_id.toString() : e.sender.user_id}`
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let ctime = new Date()
|
let ctime = new Date()
|
||||||
previousConversation = (key ? await redis.get(key) : null) || JSON.stringify({
|
previousConversation = (key ? await redis.get(key) : null) || JSON.stringify({
|
||||||
|
|
@ -1387,155 +1425,38 @@ export class chatgpt extends plugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
async chatgpt1 (e) {
|
async chatgpt1 (e) {
|
||||||
if (!Config.allowOtherMode) {
|
return await this.otherMode(e, 'api', '#chat1')
|
||||||
return false
|
|
||||||
}
|
|
||||||
let ats = e.message.filter(m => m.type === 'at')
|
|
||||||
if (!(e.atme || e.atBot) && ats.length > 0) {
|
|
||||||
if (Config.debug) {
|
|
||||||
logger.mark('艾特别人了,没艾特我,忽略#chat1')
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
let prompt = _.replace(e.raw_message.trimStart(), '#chat1', '').trim()
|
|
||||||
if (prompt.length === 0) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
await this.abstractChat(e, prompt, 'api')
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async chatgpt3 (e) {
|
async chatgpt3 (e) {
|
||||||
if (!Config.allowOtherMode) {
|
return await this.otherMode(e, 'api3', '#chat3')
|
||||||
return false
|
|
||||||
}
|
|
||||||
let ats = e.message.filter(m => m.type === 'at')
|
|
||||||
if (!(e.atme || e.atBot) && ats.length > 0) {
|
|
||||||
if (Config.debug) {
|
|
||||||
logger.mark('艾特别人了,没艾特我,忽略#chat3')
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
let prompt = _.replace(e.raw_message.trimStart(), '#chat3', '').trim()
|
|
||||||
if (prompt.length === 0) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
await this.abstractChat(e, prompt, 'api3')
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async chatglm (e) {
|
async chatglm (e) {
|
||||||
if (!Config.allowOtherMode) {
|
return await this.otherMode(e, 'chatglm')
|
||||||
return false
|
|
||||||
}
|
|
||||||
let ats = e.message.filter(m => m.type === 'at')
|
|
||||||
if (!(e.atme || e.atBot) && ats.length > 0) {
|
|
||||||
if (Config.debug) {
|
|
||||||
logger.mark('艾特别人了,没艾特我,忽略#chatglm')
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
let prompt = _.replace(e.raw_message.trimStart(), '#chatglm', '').trim()
|
|
||||||
if (prompt.length === 0) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
await this.abstractChat(e, prompt, 'chatglm')
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async bing (e) {
|
async bing (e) {
|
||||||
if (!Config.allowOtherMode) {
|
return await this.otherMode(e, 'bing')
|
||||||
return false
|
|
||||||
}
|
|
||||||
let ats = e.message.filter(m => m.type === 'at')
|
|
||||||
if (!(e.atme || e.atBot) && ats.length > 0) {
|
|
||||||
if (Config.debug) {
|
|
||||||
logger.mark('艾特别人了,没艾特我,忽略#bing')
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
let prompt = _.replace(e.raw_message.trimStart(), '#bing', '').trim()
|
|
||||||
if (prompt.length === 0) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
await this.abstractChat(e, prompt, 'bing')
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async claude2 (e) {
|
async claude2 (e) {
|
||||||
if (!Config.allowOtherMode) {
|
return await this.otherMode(e, 'claude2')
|
||||||
return false
|
|
||||||
}
|
|
||||||
let ats = e.message.filter(m => m.type === 'at')
|
|
||||||
if (!(e.atme || e.atBot) && ats.length > 0) {
|
|
||||||
if (Config.debug) {
|
|
||||||
logger.mark('艾特别人了,没艾特我,忽略#claude2')
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
let prompt = _.replace(e.raw_message.trimStart(), '#claude2', '').trim()
|
|
||||||
if (prompt.length === 0) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
await this.abstractChat(e, prompt, 'claude2')
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async claude (e) {
|
async claude (e) {
|
||||||
if (!Config.allowOtherMode) {
|
return await this.otherMode(e, 'claude')
|
||||||
return false
|
|
||||||
}
|
|
||||||
let ats = e.message.filter(m => m.type === 'at')
|
|
||||||
if (!(e.atme || e.atBot) && ats.length > 0) {
|
|
||||||
if (Config.debug) {
|
|
||||||
logger.mark('艾特别人了,没艾特我,忽略#claude')
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
let prompt = _.replace(e.raw_message.trimStart(), '#claude', '').trim()
|
|
||||||
if (prompt.length === 0) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
await this.abstractChat(e, prompt, 'claude')
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async qwen (e) {
|
async qwen (e) {
|
||||||
if (!Config.allowOtherMode) {
|
return await this.otherMode(e, 'gemini')
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
let ats = e.message.filter(m => m.type === 'at')
|
async gemini (e) {
|
||||||
if (!(e.atme || e.atBot) && ats.length > 0) {
|
return await this.otherMode(e, 'gemini')
|
||||||
if (Config.debug) {
|
|
||||||
logger.mark('艾特别人了,没艾特我,忽略#xh')
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
let prompt = _.replace(e.raw_message.trimStart(), '#qwen', '').trim()
|
|
||||||
if (prompt.length === 0) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
await this.abstractChat(e, prompt, 'qwen')
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async xh (e) {
|
async xh (e) {
|
||||||
if (!Config.allowOtherMode) {
|
return await this.otherMode(e, 'xh')
|
||||||
return false
|
|
||||||
}
|
|
||||||
let ats = e.message.filter(m => m.type === 'at')
|
|
||||||
if (!(e.atme || e.atBot) && ats.length > 0) {
|
|
||||||
if (Config.debug) {
|
|
||||||
logger.mark('艾特别人了,没艾特我,忽略#xh')
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
let prompt = _.replace(e.raw_message.trimStart(), '#xh', '').trim()
|
|
||||||
if (prompt.length === 0) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
await this.abstractChat(e, prompt, 'xh')
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async cacheContent (e, use, content, prompt, quote = [], mood = '', suggest = '', imgUrls = []) {
|
async cacheContent (e, use, content, prompt, quote = [], mood = '', suggest = '', imgUrls = []) {
|
||||||
|
|
@ -1639,7 +1560,6 @@ export class chatgpt extends plugin {
|
||||||
}
|
}
|
||||||
// 重新拿存储的token,因为可能之前有过期的被删了
|
// 重新拿存储的token,因为可能之前有过期的被删了
|
||||||
let abtrs = await getAvailableBingToken(conversation, throttledTokens)
|
let abtrs = await getAvailableBingToken(conversation, throttledTokens)
|
||||||
if (Config.toneStyle === 'Sydney' || Config.toneStyle === 'Custom') {
|
|
||||||
bingToken = abtrs.bingToken
|
bingToken = abtrs.bingToken
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
allThrottled = abtrs.allThrottled
|
allThrottled = abtrs.allThrottled
|
||||||
|
|
@ -1689,23 +1609,6 @@ export class chatgpt extends plugin {
|
||||||
logger.warn('读取文件内容出错, 忽略文件内容', err)
|
logger.warn('读取文件内容出错, 忽略文件内容', err)
|
||||||
}
|
}
|
||||||
opt.toSummaryFileContent = toSummaryFileContent
|
opt.toSummaryFileContent = toSummaryFileContent
|
||||||
} else {
|
|
||||||
// 重新创建client,因为token可能换到别的了
|
|
||||||
if (bingToken?.indexOf('=') > -1) {
|
|
||||||
cookies = bingToken
|
|
||||||
}
|
|
||||||
let bingOption = {
|
|
||||||
userToken: abtrs.bingToken, // "_U" cookie from bing.com
|
|
||||||
cookies,
|
|
||||||
debug: Config.debug,
|
|
||||||
proxy: Config.proxy,
|
|
||||||
host: Config.sydneyReverseProxy
|
|
||||||
}
|
|
||||||
if (Config.proxy && Config.sydneyReverseProxy && !Config.sydneyForceUseReverse) {
|
|
||||||
delete bingOption.host
|
|
||||||
}
|
|
||||||
bingAIClient = new BingAIClient(bingOption)
|
|
||||||
}
|
|
||||||
// 写入图片数据
|
// 写入图片数据
|
||||||
if (Config.sydneyImageRecognition) {
|
if (Config.sydneyImageRecognition) {
|
||||||
const image = await getImg(e)
|
const image = await getImg(e)
|
||||||
|
|
@ -2136,6 +2039,26 @@ export class chatgpt extends plugin {
|
||||||
images: response.images
|
images: response.images
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case 'gemini': {
|
||||||
|
let client = new GoogleGeminiClient({
|
||||||
|
e,
|
||||||
|
userId: e.sender.user_id,
|
||||||
|
key: Config.geminiKey,
|
||||||
|
model: Config.geminiModel
|
||||||
|
})
|
||||||
|
let option = {
|
||||||
|
stream: false,
|
||||||
|
onProgress: (data) => {
|
||||||
|
if (Config.debug) {
|
||||||
|
logger.info(data)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
parentMessageId: conversation.parentMessageId,
|
||||||
|
conversationId: conversation.conversationId,
|
||||||
|
system: Config.geminiPrompt
|
||||||
|
}
|
||||||
|
return await client.sendMessage(prompt, option)
|
||||||
|
}
|
||||||
default: {
|
default: {
|
||||||
// openai api
|
// openai api
|
||||||
let completionParams = {}
|
let completionParams = {}
|
||||||
|
|
@ -2703,6 +2626,25 @@ export class chatgpt extends plugin {
|
||||||
}
|
}
|
||||||
return await this.chatGPTApi.sendMessage(prompt, sendMessageOption)
|
return await this.chatGPTApi.sendMessage(prompt, sendMessageOption)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async otherMode (e, mode, pattern = `#${mode}`) {
|
||||||
|
if (!Config.allowOtherMode) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
let ats = e.message.filter(m => m.type === 'at')
|
||||||
|
if (!(e.atme || e.atBot) && ats.length > 0) {
|
||||||
|
if (Config.debug) {
|
||||||
|
logger.mark('艾特别人了,没艾特我,忽略' + pattern)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
let prompt = _.replace(e.raw_message.trimStart(), pattern, '').trim()
|
||||||
|
if (prompt.length === 0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
await this.abstractChat(e, prompt, mode)
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getAvailableBingToken (conversation, throttled = []) {
|
async function getAvailableBingToken (conversation, throttled = []) {
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,11 @@ export class ChatgptManagement extends plugin {
|
||||||
fnc: 'useClaudeAISolution',
|
fnc: 'useClaudeAISolution',
|
||||||
permission: 'master'
|
permission: 'master'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
reg: '^#chatgpt切换(Gemini|gemini)$',
|
||||||
|
fnc: 'useGeminiSolution',
|
||||||
|
permission: 'master'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
reg: '^#chatgpt切换星火$',
|
reg: '^#chatgpt切换星火$',
|
||||||
fnc: 'useXinghuoBasedSolution',
|
fnc: 'useXinghuoBasedSolution',
|
||||||
|
|
@ -184,6 +189,11 @@ export class ChatgptManagement extends plugin {
|
||||||
fnc: 'setAPIKey',
|
fnc: 'setAPIKey',
|
||||||
permission: 'master'
|
permission: 'master'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
reg: '^#chatgpt设置(Gemini|gemini)(Key|key)$',
|
||||||
|
fnc: 'setGeminiKey',
|
||||||
|
permission: 'master'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
reg: '^#chatgpt设置(API|api)设定$',
|
reg: '^#chatgpt设置(API|api)设定$',
|
||||||
fnc: 'setAPIPromptPrefix',
|
fnc: 'setAPIPromptPrefix',
|
||||||
|
|
@ -902,6 +912,16 @@ azure语音:Azure 语音是微软 Azure 平台提供的一项语音服务,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async useGeminiSolution () {
|
||||||
|
let use = await redis.get('CHATGPT:USE')
|
||||||
|
if (use !== 'gemini') {
|
||||||
|
await redis.set('CHATGPT:USE', 'gemini')
|
||||||
|
await this.reply('已切换到基于Google Gemini的解决方案')
|
||||||
|
} else {
|
||||||
|
await this.reply('当前已经是gemini模式了')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async useXinghuoBasedSolution () {
|
async useXinghuoBasedSolution () {
|
||||||
let use = await redis.get('CHATGPT:USE')
|
let use = await redis.get('CHATGPT:USE')
|
||||||
if (use !== 'xh') {
|
if (use !== 'xh') {
|
||||||
|
|
@ -1148,6 +1168,21 @@ azure语音:Azure 语音是微软 Azure 平台提供的一项语音服务,
|
||||||
this.finish('saveAPIKey')
|
this.finish('saveAPIKey')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async setGeminiKey (e) {
|
||||||
|
this.setContext('saveGeminiKey')
|
||||||
|
await this.reply('请发送Gemini API Key.获取地址:https://makersuite.google.com/app/apikey', true)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
async saveGeminiKey () {
|
||||||
|
if (!this.e.msg) return
|
||||||
|
let token = this.e.msg
|
||||||
|
// todo
|
||||||
|
Config.geminiKey = token
|
||||||
|
await this.reply('请发送Gemini API Key设置成功', true)
|
||||||
|
this.finish('saveGeminiKey')
|
||||||
|
}
|
||||||
|
|
||||||
async setXinghuoToken () {
|
async setXinghuoToken () {
|
||||||
this.setContext('saveXinghuoToken')
|
this.setContext('saveXinghuoToken')
|
||||||
await this.reply('请发送星火的ssoSessionId', true)
|
await this.reply('请发送星火的ssoSessionId', true)
|
||||||
|
|
|
||||||
|
|
@ -158,7 +158,8 @@ export class help extends plugin {
|
||||||
api: 'promptPrefixOverride',
|
api: 'promptPrefixOverride',
|
||||||
Custom: 'sydney',
|
Custom: 'sydney',
|
||||||
claude: 'slackClaudeGlobalPreset',
|
claude: 'slackClaudeGlobalPreset',
|
||||||
qwen: 'promptPrefixOverride'
|
qwen: 'promptPrefixOverride',
|
||||||
|
gemini: 'geminiPrompt'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keyMap[use]) {
|
if (keyMap[use]) {
|
||||||
|
|
@ -171,7 +172,7 @@ export class help extends plugin {
|
||||||
await redis.set(`CHATGPT:PROMPT_USE_${use}`, promptName)
|
await redis.set(`CHATGPT:PROMPT_USE_${use}`, promptName)
|
||||||
await e.reply(`你当前正在使用${use}模式,已将该模式设定应用为"${promptName}"。更该设定后建议结束对话以使设定更好生效`, true)
|
await e.reply(`你当前正在使用${use}模式,已将该模式设定应用为"${promptName}"。更该设定后建议结束对话以使设定更好生效`, true)
|
||||||
} else {
|
} else {
|
||||||
await e.reply(`你当前正在使用${use}模式,该模式不支持设定。支持设定的模式有:API、自定义、Claude`, true)
|
await e.reply(`你当前正在使用${use}模式,该模式不支持设定。支持设定的模式有:API、自定义、Claude、通义千问和Gemini`, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,11 +16,13 @@ export class BaseClient {
|
||||||
this.maxToken = 4096
|
this.maxToken = 4096
|
||||||
this.tools = []
|
this.tools = []
|
||||||
const {
|
const {
|
||||||
e, getMessageById, upsertMessage
|
e, getMessageById, upsertMessage, deleteMessageById, userId
|
||||||
} = props
|
} = props
|
||||||
this.e = e
|
this.e = e
|
||||||
this.getMessageById = getMessageById
|
this.getMessageById = getMessageById
|
||||||
this.upsertMessage = upsertMessage
|
this.upsertMessage = upsertMessage
|
||||||
|
this.deleteMessageById = deleteMessageById || (() => {})
|
||||||
|
this.userId = userId
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -42,14 +44,23 @@ export class BaseClient {
|
||||||
*/
|
*/
|
||||||
upsertMessage
|
upsertMessage
|
||||||
|
|
||||||
|
/**
|
||||||
|
* delete a message with the id
|
||||||
|
*
|
||||||
|
* @type function
|
||||||
|
* @param {string} id
|
||||||
|
* @return {Promise<void>}
|
||||||
|
*/
|
||||||
|
deleteMessageById
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send prompt message with history and return response message \
|
* Send prompt message with history and return response message \
|
||||||
* if function called, handled internally \
|
* if function called, handled internally \
|
||||||
* override this method to implement logic of sending and receiving message
|
* override this method to implement logic of sending and receiving message
|
||||||
*
|
*
|
||||||
* @param msg
|
* @param {string} msg
|
||||||
* @param opt other options, optional fields: [conversationId, parentMessageId], if not set, random uuid instead
|
* @param {{conversationId: string?, parentMessageId: string?, stream: boolean?, onProgress: function?}} opt other options, optional fields: [conversationId, parentMessageId], if not set, random uuid instead
|
||||||
* @returns {Promise<Message>} required fields: [text, conversationId, parentMessageId, id]
|
* @returns {Promise<{text, conversationId, parentMessageId, id}>} required fields: [text, conversationId, parentMessageId, id]
|
||||||
*/
|
*/
|
||||||
async sendMessage (msg, opt = {}) {
|
async sendMessage (msg, opt = {}) {
|
||||||
throw new Error('not implemented in abstract client')
|
throw new Error('not implemented in abstract client')
|
||||||
|
|
@ -60,11 +71,12 @@ export class BaseClient {
|
||||||
* override this method to implement logic of getting history
|
* override this method to implement logic of getting history
|
||||||
* keyv with local file or redis recommended
|
* keyv with local file or redis recommended
|
||||||
*
|
*
|
||||||
* @param userId such as qq number
|
* @param userId optional, such as qq number
|
||||||
* @param opt other options
|
* @param parentMessageId if blank, no history
|
||||||
* @returns {Promise<void>}
|
* @param opt optional, other options
|
||||||
|
* @returns {Promise<object[]>}
|
||||||
*/
|
*/
|
||||||
async getHistory (userId, opt = {}) {
|
async getHistory (parentMessageId, userId = this.userId, opt = {}) {
|
||||||
throw new Error('not implemented in abstract client')
|
throw new Error('not implemented in abstract client')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
155
client/GoogleGeminiClient.js
Normal file
155
client/GoogleGeminiClient.js
Normal file
|
|
@ -0,0 +1,155 @@
|
||||||
|
import { BaseClient } from './BaseClient.js'
|
||||||
|
|
||||||
|
import { getMessageById, upsertMessage } from '../utils/common.js'
|
||||||
|
import crypto from 'crypto'
|
||||||
|
let GoogleGenerativeAI, HarmBlockThreshold, HarmCategory
|
||||||
|
try {
|
||||||
|
const GenerativeAI = await import('@google/generative-ai')
|
||||||
|
GoogleGenerativeAI = GenerativeAI.GoogleGenerativeAI
|
||||||
|
HarmBlockThreshold = GenerativeAI.HarmBlockThreshold
|
||||||
|
HarmCategory = GenerativeAI.HarmCategory
|
||||||
|
} catch (err) {
|
||||||
|
console.warn('未安装@google/generative-ai,无法使用Gemini,请在chatgpt-plugin目录下执行pnpm i安装新依赖')
|
||||||
|
}
|
||||||
|
export class GoogleGeminiClient extends BaseClient {
|
||||||
|
constructor (props) {
|
||||||
|
if (!props.upsertMessage) {
|
||||||
|
props.upsertMessage = async function umGemini (message) {
|
||||||
|
return await upsertMessage(message, 'Gemini')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!props.getMessageById) {
|
||||||
|
props.getMessageById = async function umGemini (message) {
|
||||||
|
return await getMessageById(message, 'Gemini')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super(props)
|
||||||
|
this._key = props.key
|
||||||
|
this._client = new GoogleGenerativeAI(this._key)
|
||||||
|
this.model = this._client.getGenerativeModel({ model: props.model })
|
||||||
|
this.supportFunction = false
|
||||||
|
}
|
||||||
|
|
||||||
|
async getHistory (parentMessageId, userId = this.userId, opt = {}) {
|
||||||
|
const history = []
|
||||||
|
let cursor = parentMessageId
|
||||||
|
if (!cursor) {
|
||||||
|
return history
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
let parentMessage = await this.getMessageById(cursor)
|
||||||
|
if (!parentMessage) {
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
history.push(parentMessage)
|
||||||
|
cursor = parentMessage.parentMessageId
|
||||||
|
if (!cursor) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (true)
|
||||||
|
return history.reverse()
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendMessage (text, opt) {
|
||||||
|
let history = await this.getHistory(opt.parentMessageId)
|
||||||
|
let systemMessage = opt.system
|
||||||
|
if (systemMessage) {
|
||||||
|
history = history.reverse()
|
||||||
|
history.push({
|
||||||
|
role: 'model',
|
||||||
|
parts: 'ok'
|
||||||
|
})
|
||||||
|
history.push({
|
||||||
|
role: 'user',
|
||||||
|
parts: systemMessage
|
||||||
|
})
|
||||||
|
history = history.reverse()
|
||||||
|
}
|
||||||
|
const idUser = crypto.randomUUID()
|
||||||
|
const idModel = crypto.randomUUID()
|
||||||
|
let responseText = ''
|
||||||
|
try {
|
||||||
|
const chat = this.model.startChat({
|
||||||
|
history,
|
||||||
|
// [
|
||||||
|
// {
|
||||||
|
// role: 'user',
|
||||||
|
// parts: 'Hello, I have 2 dogs in my house.'
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// role: 'model',
|
||||||
|
// parts: 'Great to meet you. What would you like to know?'
|
||||||
|
// }
|
||||||
|
// ],
|
||||||
|
generationConfig: {
|
||||||
|
// todo configuration
|
||||||
|
maxOutputTokens: 1000,
|
||||||
|
temperature: 0.9,
|
||||||
|
topP: 0.1,
|
||||||
|
topK: 16
|
||||||
|
},
|
||||||
|
safetySettings: [
|
||||||
|
// todo configuration
|
||||||
|
{
|
||||||
|
category: HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT,
|
||||||
|
threshold: HarmBlockThreshold.BLOCK_NONE
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: HarmCategory.HARM_CATEGORY_HARASSMENT,
|
||||||
|
threshold: HarmBlockThreshold.BLOCK_NONE
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT,
|
||||||
|
threshold: HarmBlockThreshold.BLOCK_NONE
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: HarmCategory.HARM_CATEGORY_HATE_SPEECH,
|
||||||
|
threshold: HarmBlockThreshold.BLOCK_NONE
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
if (opt.stream && (typeof opt.onProgress === 'function')) {
|
||||||
|
const result = await chat.sendMessageStream(text)
|
||||||
|
responseText = ''
|
||||||
|
for await (const chunk of result.stream) {
|
||||||
|
const chunkText = chunk.text()
|
||||||
|
responseText += chunkText
|
||||||
|
await opt.onProgress(responseText)
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
text: responseText,
|
||||||
|
conversationId: '',
|
||||||
|
parentMessageId: idUser,
|
||||||
|
id: idModel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const result = await chat.sendMessage(text)
|
||||||
|
const response = await result.response
|
||||||
|
responseText = response.text()
|
||||||
|
return {
|
||||||
|
text: responseText,
|
||||||
|
conversationId: '',
|
||||||
|
parentMessageId: idUser,
|
||||||
|
id: idModel
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
await this.upsertMessage({
|
||||||
|
role: 'user',
|
||||||
|
parts: text,
|
||||||
|
id: idUser,
|
||||||
|
parentMessageId: opt.parentMessageId || undefined
|
||||||
|
})
|
||||||
|
await this.upsertMessage({
|
||||||
|
role: 'model',
|
||||||
|
parts: responseText,
|
||||||
|
id: idModel,
|
||||||
|
parentMessageId: idUser
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async destroyHistory (conversationId, opt = {}) {
|
||||||
|
// todo clean history
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -743,15 +743,36 @@ export function supportGuoba () {
|
||||||
component: 'Switch'
|
component: 'Switch'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '以下为杂七杂八的配置',
|
label: '以下为Gemini方式的配置',
|
||||||
component: 'Divider'
|
component: 'Divider'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: '2captchaToken',
|
field: 'geminiKey',
|
||||||
label: '验证码平台Token',
|
label: 'API密钥',
|
||||||
bottomHelpMessage: '可注册2captcha实现跳过验证码,收费服务但很便宜。否则可能会遇到验证码而卡住',
|
bottomHelpMessage: '前往https://makersuite.google.com/app/apikey获取',
|
||||||
component: 'InputPassword'
|
component: 'InputPassword'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
field: 'geminiModel',
|
||||||
|
label: '模型',
|
||||||
|
bottomHelpMessage: '目前仅支持gemini-pro',
|
||||||
|
component: 'Input'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'geminiPrompt',
|
||||||
|
label: '设定',
|
||||||
|
component: 'InputTextArea'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '以下为杂七杂八的配置',
|
||||||
|
component: 'Divider'
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// field: '2captchaToken',
|
||||||
|
// label: '验证码平台Token',
|
||||||
|
// bottomHelpMessage: '可注册2captcha实现跳过验证码,收费服务但很便宜。否则可能会遇到验证码而卡住',
|
||||||
|
// component: 'InputPassword'
|
||||||
|
// },
|
||||||
{
|
{
|
||||||
field: 'ttsSpace',
|
field: 'ttsSpace',
|
||||||
label: 'vits-uma-genshin-honkai语音转换API地址',
|
label: 'vits-uma-genshin-honkai语音转换API地址',
|
||||||
|
|
|
||||||
15
package.json
15
package.json
|
|
@ -8,11 +8,9 @@
|
||||||
"@fastify/cors": "^8.2.0",
|
"@fastify/cors": "^8.2.0",
|
||||||
"@fastify/static": "^6.9.0",
|
"@fastify/static": "^6.9.0",
|
||||||
"@fastify/websocket": "^8.2.0",
|
"@fastify/websocket": "^8.2.0",
|
||||||
|
"@google/generative-ai": "^0.1.1",
|
||||||
"@slack/bolt": "^3.13.2",
|
"@slack/bolt": "^3.13.2",
|
||||||
"@waylaidwanderer/chatgpt-api": "^1.37.1",
|
|
||||||
"asn1.js": "^5.0.0",
|
"asn1.js": "^5.0.0",
|
||||||
"chatgpt": "^5.2.4",
|
|
||||||
"crypto": "^1.0.1",
|
|
||||||
"delay": "^6.0.0",
|
"delay": "^6.0.0",
|
||||||
"diff": "^5.1.0",
|
"diff": "^5.1.0",
|
||||||
"emoji-strip": "^1.0.1",
|
"emoji-strip": "^1.0.1",
|
||||||
|
|
@ -24,6 +22,7 @@
|
||||||
"js-tiktoken": "^1.0.5",
|
"js-tiktoken": "^1.0.5",
|
||||||
"keyv": "^4.5.3",
|
"keyv": "^4.5.3",
|
||||||
"keyv-file": "^0.2.0",
|
"keyv-file": "^0.2.0",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
"microsoft-cognitiveservices-speech-sdk": "1.32.0",
|
"microsoft-cognitiveservices-speech-sdk": "1.32.0",
|
||||||
"node-fetch": "^3.3.1",
|
"node-fetch": "^3.3.1",
|
||||||
"openai": "^3.2.1",
|
"openai": "^3.2.1",
|
||||||
|
|
@ -35,18 +34,18 @@
|
||||||
"ws": "^8.13.0"
|
"ws": "^8.13.0"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"xlsx": "^0.18.5",
|
|
||||||
"mammoth": "^1.6.0",
|
|
||||||
"pdfjs-dist": "^3.11.174",
|
|
||||||
"nodejs-pptx": "^1.2.4",
|
|
||||||
"@node-rs/jieba": "^1.6.2",
|
"@node-rs/jieba": "^1.6.2",
|
||||||
"cycletls": "^1.0.21",
|
"cycletls": "^1.0.21",
|
||||||
"jimp": "^0.22.7",
|
"jimp": "^0.22.7",
|
||||||
|
"mammoth": "^1.6.0",
|
||||||
"node-silk": "^0.1.0",
|
"node-silk": "^0.1.0",
|
||||||
|
"nodejs-pptx": "^1.2.4",
|
||||||
|
"pdfjs-dist": "^3.11.174",
|
||||||
"puppeteer-extra": "^3.3.6",
|
"puppeteer-extra": "^3.3.6",
|
||||||
"puppeteer-extra-plugin-recaptcha": "^3.6.8",
|
"puppeteer-extra-plugin-recaptcha": "^3.6.8",
|
||||||
"puppeteer-extra-plugin-stealth": "^2.11.2",
|
"puppeteer-extra-plugin-stealth": "^2.11.2",
|
||||||
"sharp": "^0.32.3"
|
"sharp": "^0.32.3",
|
||||||
|
"xlsx": "^0.18.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
|
|
|
||||||
26
patches/@google__generative-ai@0.1.1.patch
Normal file
26
patches/@google__generative-ai@0.1.1.patch
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
diff --git a/dist/index.js b/dist/index.js
|
||||||
|
index c71c104e7b8ee70ed1b5a5141d04c98109fe6439..2dd8b1f93de0e502729cb91c9618bf80e8559e1e 100644
|
||||||
|
--- a/dist/index.js
|
||||||
|
+++ b/dist/index.js
|
||||||
|
@@ -152,7 +152,7 @@ class GoogleGenerativeAIResponseError extends GoogleGenerativeAIError {
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
-const BASE_URL = "https://generativelanguage.googleapis.com";
|
||||||
|
+const BASE_URL = "https://gemini.ikechan8370.com";
|
||||||
|
const API_VERSION = "v1";
|
||||||
|
/**
|
||||||
|
* We can't `require` package.json if this runs on web. We will use rollup to
|
||||||
|
diff --git a/dist/index.mjs b/dist/index.mjs
|
||||||
|
index 402a0c7fa5b692dea07d2dfd83e0148f0a493ca2..c48ce6d612a8752a5161da574804e7a830700d2c 100644
|
||||||
|
--- a/dist/index.mjs
|
||||||
|
+++ b/dist/index.mjs
|
||||||
|
@@ -150,7 +150,7 @@ class GoogleGenerativeAIResponseError extends GoogleGenerativeAIError {
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
-const BASE_URL = "https://generativelanguage.googleapis.com";
|
||||||
|
+const BASE_URL = "https://gemini.ikechan8370.com";
|
||||||
|
const API_VERSION = "v1";
|
||||||
|
/**
|
||||||
|
* We can't `require` package.json if this runs on web. We will use rollup to
|
||||||
|
|
@ -162,6 +162,9 @@ const defaultConfig = {
|
||||||
qwenSeed: 0,
|
qwenSeed: 0,
|
||||||
qwenTemperature: 1,
|
qwenTemperature: 1,
|
||||||
qwenEnableSearch: true,
|
qwenEnableSearch: true,
|
||||||
|
geminiKey: '',
|
||||||
|
geminiModel: 'gemini-pro',
|
||||||
|
geminiPrompt: 'You are Gemini. Your answer shouldn\'t be too verbose. Prefer to answer in Chinese.',
|
||||||
version: 'v2.7.8'
|
version: 'v2.7.8'
|
||||||
}
|
}
|
||||||
const _path = process.cwd()
|
const _path = process.cwd()
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { Config } from './config.js'
|
import { Config } from './config.js'
|
||||||
import { ChatGPTAPI } from 'chatgpt'
|
import { ChatGPTAPI } from './openai/chatgpt-api.js'
|
||||||
import fetch from 'node-fetch'
|
import fetch from 'node-fetch'
|
||||||
import { getProxy } from './proxy.js'
|
import { getProxy } from './proxy.js'
|
||||||
let proxy = getProxy()
|
let proxy = getProxy()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue