Skip to content

Commit

Permalink
Squashed commit of the following:
Browse files Browse the repository at this point in the history
commit 41762a1
Merge: e0d2e34 a786fa4
Author: zhayujie <yjzha1996@163.com>
Date:   Fri Jul 21 17:18:56 2023 +0800

    Merge pull request zhayujie#1332 from zhayujie/feat-1.3.3

    fix: reduce memory usage

commit a786fa4
Author: zhayujie <yjzha1996@163.com>
Date:   Fri Jul 21 17:16:34 2023 +0800

    fix: reduce the expiration time and avoid storing the original message text to decrease memory usage

commit e4c7602
Author: zhayujie <yjzha1996@163.com>
Date:   Fri Jul 21 17:14:11 2023 +0800

    docs: update README.md

commit e0d2e34
Merge: 2361e3c 9ef8e1b
Author: zhayujie <yjzha1996@163.com>
Date:   Fri Jul 21 10:50:16 2023 +0800

    Merge pull request zhayujie#1328 from zhayujie/feat-1.3.3

    feat: support global plugin config for docker env

commit 9ef8e1b
Author: zhayujie <yjzha1996@163.com>
Date:   Thu Jul 20 16:08:19 2023 +0800

    feat: move loading config method to base class

commit aae9b64
Author: zhayujie <yjzha1996@163.com>
Date:   Thu Jul 20 14:46:41 2023 +0800

    fix: reduce unnecessary error traceback logs

commit 4bab429
Author: zhayujie <yjzha1996@163.com>
Date:   Thu Jul 20 14:24:40 2023 +0800

    fix: global plugin config read

commit 954e55f
Author: zhayujie <yjzha1996@163.com>
Date:   Thu Jul 20 11:36:02 2023 +0800

    feat: add plugin global config to support docker volumes

commit 2361e3c
Author: zhayujie <yjzha1996@163.com>
Date:   Wed Jul 19 18:23:59 2023 +0800

    docs: update README for railway cancelled free service

commit 8aac86f
Merge: 94b47a5 6384e93
Author: zhayujie <yjzha1996@163.com>
Date:   Wed Jul 5 01:44:06 2023 +0800

    Merge pull request zhayujie#1291 from 6vision/master

    (tool)fix azure model

commit 6384e93
Author: vision <vision_wangpc@sina.com>
Date:   Wed Jul 5 01:06:53 2023 +0800

    plugin(tool): 更新0.4.6

    1、temp fix summary tool not ending bug
    2、兼容0613 gpt-3.5
    3、add azure's model name: gpt-35-turbo

commit 7a9205d
Author: vision <vision_wangpc@sina.com>
Date:   Wed Jul 5 01:01:46 2023 +0800

    fix azure model

    更新chatgpt_tool_hub至0.4.6,拉取最新代码。tool即可使用azure接口!

commit 94b47a5
Merge: 709b5be f970b2c
Author: Jianglang <lanvent@qq.com>
Date:   Sat Jul 1 18:37:07 2023 +0800

    Merge pull request zhayujie#1282 from haikerapples/master_haiker_timetask

    内置 timetask 插件

commit 709b5be
Author: zhayujie <yjzha1996@163.com>
Date:   Sat Jul 1 13:17:08 2023 +0800

    fix: group voice config and azure model calc support

commit f970b2c
Author: haikerwang <haikerwang@tencent.com>
Date:   Thu Jun 29 00:58:57 2023 +0800

    内置 timetask 插件
  • Loading branch information
congxuma committed Jul 23, 2023
1 parent da90198 commit 0d97dc3
Show file tree
Hide file tree
Showing 14 changed files with 150 additions and 54 deletions.
23 changes: 16 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,6 @@

> 欢迎接入更多应用,参考 [Terminal代码](https://github.com/zhayujie/chatgpt-on-wechat/blob/master/channel/terminal/terminal_channel.py)实现接收和发送消息逻辑即可接入。 同时欢迎增加新的插件,参考 [插件说明文档](https://github.com/zhayujie/chatgpt-on-wechat/tree/master/plugins)
**一键部署:**
- 个人微信

[![Deploy on Railway](https://railway.app/button.svg)](https://railway.app/template/qApznZ?referralCode=RC3znh)

# 演示

https://user-images.githubusercontent.com/26161723/233777277-e3b9928e-b88f-43e2-b0e0-3cbc923bc799.mp4
Expand All @@ -32,7 +27,7 @@ Demo made by [Visionn](https://www.wangpc.cc/)

# 更新日志

>**2023.06.12:** 接入 [LinkAI](https://chat.link-ai.tech/console) 平台,可在线创建 个人知识库,并接入微信中。Beta版本欢迎体验,使用参考 [接入文档](https://link-ai.tech/platform/link-app/wechat)
>**2023.06.12:** 接入 [LinkAI](https://chat.link-ai.tech/console) 平台,可在线创建 个人知识库,并接入微信、公众号及企业微信中。使用参考 [接入文档](https://link-ai.tech/platform/link-app/wechat)
>**2023.04.26:** 支持企业微信应用号部署,兼容插件,并支持语音图片交互,私人助理理想选择,[使用文档](https://github.com/zhayujie/chatgpt-on-wechat/blob/master/channel/wechatcom/README.md)。(contributed by [@lanvent](https://github.com/lanvent) in [#944](https://github.com/zhayujie/chatgpt-on-wechat/pull/944))
Expand Down Expand Up @@ -231,14 +226,28 @@ sudo docker compose up -d
sudo docker logs -f chatgpt-on-wechat
```

#### (3) 插件使用

如果需要在docker容器中修改插件配置,可通过挂载的方式完成,将 [插件配置文件](https://github.com/zhayujie/chatgpt-on-wechat/blob/master/plugins/config.json.template)
重命名为 `config.json`,放置于 `docker-compose.yml` 相同目录下,并在 `docker-compose.yml` 中的 `chatgpt-on-wechat` 部分下添加 `volumes` 映射:

```
volumes:
- ./config.json:/app/plugins/config.json
```

### 4. Railway部署

> Railway每月提供5刀和最多500小时的免费额度。
> Railway 每月提供5刀和最多500小时的免费额度。 (07.11更新: 目前大部分账号已无法免费部署)
1. 进入 [Railway](https://railway.app/template/qApznZ?referralCode=RC3znh)
2. 点击 `Deploy Now` 按钮。
3. 设置环境变量来重载程序运行的参数,例如`open_ai_api_key`, `character_desc`

**一键部署:**

[![Deploy on Railway](https://railway.app/button.svg)](https://railway.app/template/qApznZ?referralCode=RC3znh)

## 常见问题

FAQs: <https://github.com/zhayujie/chatgpt-on-wechat/wiki/FAQs>
Expand Down
2 changes: 1 addition & 1 deletion bot/chatgpt/chat_gpt_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def num_tokens_from_messages(messages, model):

if model in ["gpt-3.5-turbo-0301", "gpt-35-turbo"]:
return num_tokens_from_messages(messages, model="gpt-3.5-turbo")
elif model in ["gpt-4-0314", "gpt-4-0613", "gpt-4-32k", "gpt-4-32k-0613", "gpt-3.5-turbo-0613", "gpt-3.5-turbo-16k", "gpt-3.5-turbo-16k-0613"]:
elif model in ["gpt-4-0314", "gpt-4-0613", "gpt-4-32k", "gpt-4-32k-0613", "gpt-3.5-turbo-0613", "gpt-3.5-turbo-16k", "gpt-3.5-turbo-16k-0613", "gpt-35-turbo-16k"]:
return num_tokens_from_messages(messages, model="gpt-4")

try:
Expand Down
6 changes: 3 additions & 3 deletions channel/wechat/wechat_channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def wrapper(self, cmsg: ChatMessage):
if msgId in self.receivedMsgs:
logger.info("Wechat message {} already received, ignore".format(msgId))
return
self.receivedMsgs[msgId] = cmsg
self.receivedMsgs[msgId] = True
create_time = cmsg.create_time # 消息时间戳
if conf().get("hot_reload") == True and int(create_time) < int(time.time()) - 60: # 跳过1分钟前的历史消息
logger.debug("[WX]history message {} skipped".format(msgId))
Expand Down Expand Up @@ -105,7 +105,7 @@ class WechatChannel(ChatChannel):

def __init__(self):
super().__init__()
self.receivedMsgs = ExpiredDict(60 * 60 * 24)
self.receivedMsgs = ExpiredDict(60 * 60)

def startup(self):
itchat.instance.receivingRetryCount = 600 # 修改断线超时时间
Expand Down Expand Up @@ -159,7 +159,7 @@ def handle_single(self, cmsg: ChatMessage):
@_check
def handle_group(self, cmsg: ChatMessage):
if cmsg.ctype == ContextType.VOICE:
if conf().get("speech_recognition") != True:
if conf().get("group_speech_recognition") != True:
return
logger.debug("[WX]receive voice for group msg: {}".format(cmsg.content))
elif cmsg.ctype == ContextType.IMAGE:
Expand Down
28 changes: 26 additions & 2 deletions config.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"single_chat_reply_suffix": "", # 私聊时自动回复的后缀,\n 可以换行
"group_chat_prefix": ["@bot"], # 群聊时包含该前缀则会触发机器人回复
"group_chat_reply_prefix": "", # 群聊时自动回复的前缀
"group_chat_reply_suffix": "", # 群聊时自动回复的后缀,\n 可以换行
"group_chat_reply_suffix": "", # 群聊时自动回复的后缀,\n 可以换行
"group_chat_keyword": [], # 群聊时包含该关键词则会触发机器人回复
"group_at_off": False, # 是否关闭群聊时@bot的触发
"group_name_white_list": ["ChatGPT测试群", "ChatGPT测试群2"], # 开启自动回复的群名称列表
Expand All @@ -38,7 +38,8 @@

# chatgpt会话参数
"expires_in_seconds": 3600, # 无操作会话的过期时间
"character_desc": "你是ChatGPT, 一个由OpenAI训练的大型语言模型, 你旨在回答并解决人们的任何问题,并且可以使用多种语言与人交流。", # 人格描述
# 人格描述
"character_desc": "你是ChatGPT, 一个由OpenAI训练的大型语言模型, 你旨在回答并解决人们的任何问题,并且可以使用多种语言与人交流。",
"conversation_max_tokens": 1000, # 支持上下文记忆的最多字符数
# chatgpt限流配置
"rate_limit_chatgpt": 20, # chatgpt的调用频率限制
Expand Down Expand Up @@ -230,3 +231,26 @@ def subscribe_msg():
trigger_prefix = conf().get("single_chat_prefix", [""])[0]
msg = conf().get("subscribe_msg", "")
return msg.format(trigger_prefix=trigger_prefix)


# global plugin config
plugin_config = {}


def write_plugin_config(pconf: dict):
"""
写入插件全局配置
:param pconf: 全量插件配置
"""
global plugin_config
for k in pconf:
plugin_config[k.lower()] = pconf[k]


def pconf(plugin_name: str) -> dict:
"""
根据插件名称获取配置
:param plugin_name: 插件名称
:return: 该插件的配置项
"""
return plugin_config.get(plugin_name.lower())
4 changes: 3 additions & 1 deletion docker/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ services:
chatgpt-on-wechat:
image: zhayujie/chatgpt-on-wechat
container_name: chatgpt-on-wechat
security_opt:
- seccomp:unconfined
environment:
OPEN_AI_API_KEY: 'YOUR API KEY'
MODEL: 'gpt-3.5-turbo'
OPEN_AI_PROXY: ''
PROXY: ''
SINGLE_CHAT_PREFIX: '["bot", "@bot"]'
SINGLE_CHAT_REPLY_PREFIX: '"[bot] "'
GROUP_CHAT_PREFIX: '["@bot"]'
Expand Down
19 changes: 10 additions & 9 deletions plugins/banwords/banwords.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,17 @@ class Banwords(Plugin):
def __init__(self):
super().__init__()
try:
# load config
conf = super().load_config()
curdir = os.path.dirname(__file__)
config_path = os.path.join(curdir, "config.json")
conf = None
if not os.path.exists(config_path):
conf = {"action": "ignore"}
with open(config_path, "w") as f:
json.dump(conf, f, indent=4)
else:
with open(config_path, "r") as f:
conf = json.load(f)
if not conf:
# 配置不存在则写入默认配置
config_path = os.path.join(curdir, "config.json")
if not os.path.exists(config_path):
conf = {"action": "ignore"}
with open(config_path, "w") as f:
json.dump(conf, f, indent=4)

self.searchr = WordsSearch()
self.action = conf["action"]
banwords_path = os.path.join(curdir, "banwords.txt")
Expand Down
8 changes: 3 additions & 5 deletions plugins/bdunit/bdunit.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,8 @@ class BDunit(Plugin):
def __init__(self):
super().__init__()
try:
curdir = os.path.dirname(__file__)
config_path = os.path.join(curdir, "config.json")
conf = None
if not os.path.exists(config_path):
conf = super().load_config()
if not conf:
raise Exception("config.json not found")
else:
with open(config_path, "r") as f:
Expand All @@ -45,7 +43,7 @@ def __init__(self):
self.service_id = os.environ.get("bdubit_service_id","")
self.api_key = os.environ.get("bdubit_api_key","")
self.secret_key = os.environ.get("bdubit_secret_key","")

self.access_token = self.get_token()
self.handlers[Event.ON_HANDLE_CONTEXT] = self.on_handle_context
logger.info("[BDunit] inited")
Expand Down
24 changes: 24 additions & 0 deletions plugins/config.json.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"godcmd": {
"password": "",
"admin_users": []
},
"banwords": {
"action": "replace",
"reply_filter": true,
"reply_action": "ignore"
},
"tool": {
"tools": [
"python",
"url-get",
"terminal",
"meteo-weather"
],
"kwargs": {
"top_k_results": 2,
"no_default": false,
"model_name": "gpt-3.5-turbo"
}
}
}
17 changes: 7 additions & 10 deletions plugins/godcmd/godcmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,16 +178,13 @@ class Godcmd(Plugin):
def __init__(self):
super().__init__()

curdir = os.path.dirname(__file__)
config_path = os.path.join(curdir, "config.json")
gconf = None
if not os.path.exists(config_path):
gconf = {"password": "", "admin_users": []}
with open(config_path, "w") as f:
json.dump(gconf, f, indent=4)
else:
with open(config_path, "r") as f:
gconf = json.load(f)
config_path = os.path.join(os.path.dirname(__file__), "config.json")
gconf = super().load_config()
if not gconf:
if not os.path.exists(config_path):
gconf = {"password": "", "admin_users": []}
with open(config_path, "w") as f:
json.dump(gconf, f, indent=4)
if gconf["password"] == "":
self.temp_password = "".join(random.sample(string.digits, 4))
logger.info("[Godcmd] 因未设置口令,本次的临时口令为%s。" % self.temp_password)
Expand Down
22 changes: 22 additions & 0 deletions plugins/plugin.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,28 @@
import os
import json
from config import pconf
from common.log import logger


class Plugin:
def __init__(self):
self.handlers = {}

def load_config(self) -> dict:
"""
加载当前插件配置
:return: 插件配置字典
"""
# 优先获取 plugins/config.json 中的全局配置
plugin_conf = pconf(self.name)
if not plugin_conf:
# 全局配置不存在,则获取插件目录下的配置
plugin_config_path = os.path.join(self.path, "config.json")
if os.path.exists(plugin_config_path):
with open(plugin_config_path, "r") as f:
plugin_conf = json.load(f)
logger.debug(f"loading plugin config, plugin_name={self.name}, conf={plugin_conf}")
return plugin_conf

def get_help_text(self, **kwargs):
return "暂无帮助信息"
30 changes: 27 additions & 3 deletions plugins/plugin_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from common.log import logger
from common.singleton import singleton
from common.sorted_dict import SortedDict
from config import conf
from config import conf, write_plugin_config

from .event import *

Expand Down Expand Up @@ -62,6 +62,28 @@ def load_config(self):
self.save_config()
return pconf

@staticmethod
def _load_all_config():
"""
背景: 目前插件配置存放于每个插件目录的config.json下,docker运行时不方便进行映射,故增加统一管理的入口,优先
加载 plugins/config.json,原插件目录下的config.json 不受影响
从 plugins/config.json 中加载所有插件的配置并写入 config.py 的全局配置中,供插件中使用
插件实例中通过 config.pconf(plugin_name) 即可获取该插件的配置
"""
all_config_path = "./plugins/config.json"
try:
if os.path.exists(all_config_path):
# read from all plugins config
with open(all_config_path, "r", encoding="utf-8") as f:
all_conf = json.load(f)
logger.info(f"load all config from plugins/config.json: {all_conf}")

# write to global config
write_plugin_config(all_conf)
except Exception as e:
logger.error(e)

def scan_plugins(self):
logger.info("Scaning plugins ...")
plugins_dir = "./plugins"
Expand All @@ -88,7 +110,7 @@ def scan_plugins(self):
self.loaded[plugin_path] = importlib.import_module(import_path)
self.current_plugin_path = None
except Exception as e:
logger.exception("Failed to import plugin %s: %s" % (plugin_name, e))
logger.warn("Failed to import plugin %s: %s" % (plugin_name, e))
continue
pconf = self.pconf
news = [self.plugins[name] for name in self.plugins]
Expand Down Expand Up @@ -123,7 +145,7 @@ def activate_plugins(self): # 生成新开启的插件实例
try:
instance = plugincls()
except Exception as e:
logger.exception("Failed to init %s, diabled. %s" % (name, e))
logger.warn("Failed to init %s, diabled. %s" % (name, e))
self.disable_plugin(name)
failed_plugins.append(name)
continue
Expand All @@ -149,6 +171,8 @@ def reload_plugin(self, name: str):
def load_plugins(self):
self.load_config()
self.scan_plugins()
# 加载全量插件配置
self._load_all_config()
pconf = self.pconf
logger.debug("plugins.json config={}".format(pconf))
for name, plugin in pconf["plugins"].items():
Expand Down
6 changes: 4 additions & 2 deletions plugins/source.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@
"summary": {
"url": "https://github.com/lanvent/plugin_summary.git",
"desc": "总结聊天记录的插件"
}
,
},
"timetask": {
"url": "https://github.com/haikerapples/timetask.git",
"desc": "一款定时任务系统的插件"},
"getnews": {
"url": "https://github.com/congxuma/plugin_getnews.git",
"desc": "测试获取新闻"
Expand Down
13 changes: 3 additions & 10 deletions plugins/tool/tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
from bridge.context import ContextType
from bridge.reply import Reply, ReplyType
from common import const
from common.log import logger
from config import conf
from plugins import *

Expand Down Expand Up @@ -119,15 +118,8 @@ def on_handle_context(self, e_context: EventContext):
return

def _read_json(self) -> dict:
curdir = os.path.dirname(__file__)
config_path = os.path.join(curdir, "config.json")
tool_config = {"tools": [], "kwargs": {}}
if not os.path.exists(config_path):
return tool_config
else:
with open(config_path, "r") as f:
tool_config = json.load(f)
return tool_config
default_config = {"tools": [], "kwargs": {}}
return super().load_config() or default_config

def _build_tool_kwargs(self, kwargs: dict):
tool_model_name = kwargs.get("model_name")
Expand Down Expand Up @@ -167,6 +159,7 @@ def _build_tool_kwargs(self, kwargs: dict):
"debug": kwargs.get("debug", False),
"openai_api_key": conf().get("open_ai_api_key", ""),
"open_ai_api_base": conf().get("open_ai_api_base", "https://api.openai.com/v1"),
"deployment_id": conf().get("azure_deployment_id", ""),
"proxy": conf().get("proxy", ""),
"request_timeout": request_timeout if request_timeout else conf().get("request_timeout", 120),
# note: 目前tool暂未对其他模型测试,但这里仍对配置来源做了优先级区分,一般插件配置可覆盖全局配置
Expand Down
2 changes: 1 addition & 1 deletion requirements-optional.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@ wechatpy
# chatgpt-tool-hub plugin

--extra-index-url https://pypi.python.org/simple
chatgpt_tool_hub==0.4.4
chatgpt_tool_hub==0.4.6

0 comments on commit 0d97dc3

Please sign in to comment.