Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

186 lines
7.5KB

  1. import json
  2. import os
  3. from chatgpt_tool_hub.apps import AppFactory
  4. from chatgpt_tool_hub.apps.app import App
  5. from chatgpt_tool_hub.tools.all_tool_list import get_all_tool_names
  6. import plugins
  7. from bridge.bridge import Bridge
  8. from bridge.context import ContextType
  9. from bridge.reply import Reply, ReplyType
  10. from common import const
  11. from common.log import logger
  12. from config import conf
  13. from plugins import *
  14. @plugins.register(
  15. name="tool",
  16. desc="Arming your ChatGPT bot with various tools",
  17. version="0.4",
  18. author="goldfishh",
  19. desire_priority=0,
  20. )
  21. class Tool(Plugin):
  22. def __init__(self):
  23. super().__init__()
  24. self.handlers[Event.ON_HANDLE_CONTEXT] = self.on_handle_context
  25. self.app = self._reset_app()
  26. logger.info("[tool] inited")
  27. def get_help_text(self, verbose=False, **kwargs):
  28. help_text = "这是一个能让chatgpt联网,搜索,数字运算的插件,将赋予强大且丰富的扩展能力。"
  29. trigger_prefix = conf().get("plugin_trigger_prefix", "$")
  30. if not verbose:
  31. return help_text
  32. help_text += "\n使用说明:\n"
  33. help_text += f"{trigger_prefix}tool " + "命令: 根据给出的{命令}使用一些可用工具尽力为你得到结果。\n"
  34. help_text += f"{trigger_prefix}tool reset: 重置工具。\n\n"
  35. help_text += f"已加载工具列表: \n"
  36. for idx, tool in enumerate(self.app.get_tool_list()):
  37. if idx != 0:
  38. help_text += ", "
  39. help_text += f"{tool}"
  40. return help_text
  41. def on_handle_context(self, e_context: EventContext):
  42. if e_context["context"].type != ContextType.TEXT:
  43. return
  44. # 暂时不支持未来扩展的bot
  45. if Bridge().get_bot_type("chat") not in (
  46. const.CHATGPT,
  47. const.OPEN_AI,
  48. const.CHATGPTONAZURE,
  49. const.LINKAI,
  50. ):
  51. return
  52. content = e_context["context"].content
  53. content_list = e_context["context"].content.split(maxsplit=1)
  54. if not content or len(content_list) < 1:
  55. e_context.action = EventAction.CONTINUE
  56. return
  57. logger.debug("[tool] on_handle_context. content: %s" % content)
  58. reply = Reply()
  59. reply.type = ReplyType.TEXT
  60. trigger_prefix = conf().get("plugin_trigger_prefix", "$")
  61. # todo: 有些工具必须要api-key,需要修改config文件,所以这里没有实现query增删tool的功能
  62. if content.startswith(f"{trigger_prefix}tool"):
  63. if len(content_list) == 1:
  64. logger.debug("[tool]: get help")
  65. reply.content = self.get_help_text()
  66. e_context["reply"] = reply
  67. e_context.action = EventAction.BREAK_PASS
  68. return
  69. elif len(content_list) > 1:
  70. if content_list[1].strip() == "reset":
  71. logger.debug("[tool]: reset config")
  72. self.app = self._reset_app()
  73. reply.content = "重置工具成功"
  74. e_context["reply"] = reply
  75. e_context.action = EventAction.BREAK_PASS
  76. return
  77. elif content_list[1].startswith("reset"):
  78. logger.debug("[tool]: remind")
  79. e_context["context"].content = "请你随机用一种聊天风格,提醒用户:如果想重置tool插件,reset之后不要加任何字符"
  80. e_context.action = EventAction.BREAK
  81. return
  82. query = content_list[1].strip()
  83. # Don't modify bot name
  84. all_sessions = Bridge().get_bot("chat").sessions
  85. user_session = all_sessions.session_query(query, e_context["context"]["session_id"]).messages
  86. # chatgpt-tool-hub will reply you with many tools
  87. logger.debug("[tool]: just-go")
  88. try:
  89. _reply = self.app.ask(query, user_session)
  90. e_context.action = EventAction.BREAK_PASS
  91. all_sessions.session_reply(_reply, e_context["context"]["session_id"])
  92. except Exception as e:
  93. logger.exception(e)
  94. logger.error(str(e))
  95. e_context["context"].content = "请你随机用一种聊天风格,提醒用户:这个问题tool插件暂时无法处理"
  96. reply.type = ReplyType.ERROR
  97. e_context.action = EventAction.BREAK
  98. return
  99. reply.content = _reply
  100. e_context["reply"] = reply
  101. return
  102. def _read_json(self) -> dict:
  103. curdir = os.path.dirname(__file__)
  104. config_path = os.path.join(curdir, "config.json")
  105. tool_config = {"tools": [], "kwargs": {}}
  106. if not os.path.exists(config_path):
  107. return tool_config
  108. else:
  109. with open(config_path, "r") as f:
  110. tool_config = json.load(f)
  111. return tool_config
  112. def _build_tool_kwargs(self, kwargs: dict):
  113. tool_model_name = kwargs.get("model_name")
  114. request_timeout = kwargs.get("request_timeout")
  115. return {
  116. "debug": kwargs.get("debug", False),
  117. "openai_api_key": conf().get("open_ai_api_key", ""),
  118. "open_ai_api_base": conf().get("open_ai_api_base", "https://api.openai.com/v1"),
  119. "proxy": conf().get("proxy", ""),
  120. "request_timeout": request_timeout if request_timeout else conf().get("request_timeout", 120),
  121. # note: 目前tool暂未对其他模型测试,但这里仍对配置来源做了优先级区分,一般插件配置可覆盖全局配置
  122. "model_name": tool_model_name if tool_model_name else conf().get("model", "gpt-3.5-turbo"),
  123. "no_default": kwargs.get("no_default", False),
  124. "top_k_results": kwargs.get("top_k_results", 3),
  125. # for news tool
  126. "news_api_key": kwargs.get("news_api_key", ""),
  127. # for bing-search tool
  128. "bing_subscription_key": kwargs.get("bing_subscription_key", ""),
  129. # for google-search tool
  130. "google_api_key": kwargs.get("google_api_key", ""),
  131. "google_cse_id": kwargs.get("google_cse_id", ""),
  132. # for searxng-search tool
  133. "searx_search_host": kwargs.get("searx_search_host", ""),
  134. # for wolfram-alpha tool
  135. "wolfram_alpha_appid": kwargs.get("wolfram_alpha_appid", ""),
  136. # for morning-news tool
  137. "morning_news_api_key": kwargs.get("morning_news_api_key", ""),
  138. # for visual_dl tool
  139. "cuda_device": kwargs.get("cuda_device", "cpu"),
  140. "think_depth": kwargs.get("think_depth", 3),
  141. "arxiv_summary": kwargs.get("arxiv_summary", True),
  142. "morning_news_use_llm": kwargs.get("morning_news_use_llm", False),
  143. }
  144. def _filter_tool_list(self, tool_list: list):
  145. valid_list = []
  146. for tool in tool_list:
  147. if tool in get_all_tool_names():
  148. valid_list.append(tool)
  149. else:
  150. logger.warning("[tool] filter invalid tool: " + repr(tool))
  151. return valid_list
  152. def _reset_app(self) -> App:
  153. tool_config = self._read_json()
  154. app_kwargs = self._build_tool_kwargs(tool_config.get("kwargs", {}))
  155. app = AppFactory()
  156. app.init_env(**app_kwargs)
  157. # filter not support tool
  158. tool_list = self._filter_tool_list(tool_config.get("tools", []))
  159. return app.create_app(tools_list=tool_list, **app_kwargs)