Skip to content

Commit

Permalink
Merge pull request #2363 from 6vision/linkai_plugin
Browse files Browse the repository at this point in the history
Summary and MJ  support can be configured through LinkAI platform app plugins
  • Loading branch information
6vision authored Oct 31, 2024
2 parents 1989d53 + 04ef090 commit d90eeb7
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 21 deletions.
2 changes: 2 additions & 0 deletions plugins/linkai/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@

如果不想创建 `plugins/linkai/config.json` 配置,可以直接通过 `$linkai sum open` 指令开启该功能。

也可以通过私聊(全局 `config.json` 中的 `linkai_app_code`)或者群聊绑定(通过`group_app_map`参数配置)的应用来开启该功能:在LinkAI平台 [应用配置](https://link-ai.tech/console/factory) 里添加并开启**内容总结**插件。

#### 使用

功能开启后,向机器人发送 **文件****分享链接卡片****图片** 即可生成摘要,进一步可以与文件或链接的内容进行多轮对话。如果需要关闭某种类型的内容总结,设置 `summary`配置中的type字段即可。
Expand Down
61 changes: 48 additions & 13 deletions plugins/linkai/linkai.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from common import const
import os
from .utils import Util
from config import plugin_config
from config import plugin_config, conf


@plugins.register(
Expand All @@ -28,7 +28,7 @@ def __init__(self):
# 未加载到配置,使用模板中的配置
self.config = self._load_config_template()
if self.config:
self.mj_bot = MJBot(self.config.get("midjourney"))
self.mj_bot = MJBot(self.config.get("midjourney"), self._fetch_group_app_code)
self.sum_config = {}
if self.config:
self.sum_config = self.config.get("summary")
Expand Down Expand Up @@ -56,7 +56,8 @@ def on_handle_context(self, e_context: EventContext):
return
if context.type != ContextType.IMAGE:
_send_info(e_context, "正在为你加速生成摘要,请稍后")
res = LinkSummary().summary_file(file_path)
app_code = self._fetch_app_code(context)
res = LinkSummary().summary_file(file_path, app_code)
if not res:
if context.type != ContextType.IMAGE:
_set_reply_text("因为神秘力量无法获取内容,请稍后再试吧", e_context, level=ReplyType.TEXT)
Expand All @@ -74,7 +75,8 @@ def on_handle_context(self, e_context: EventContext):
if not LinkSummary().check_url(context.content):
return
_send_info(e_context, "正在为你加速生成摘要,请稍后")
res = LinkSummary().summary_url(context.content)
app_code = self._fetch_app_code(context)
res = LinkSummary().summary_url(context.content, app_code)
if not res:
_set_reply_text("因为神秘力量无法获取文章内容,请稍后再试吧~", e_context, level=ReplyType.TEXT)
return
Expand Down Expand Up @@ -169,7 +171,7 @@ def _process_admin_cmd(self, e_context: EventContext):
return

if len(cmd) == 3 and cmd[1] == "sum" and (cmd[2] == "open" or cmd[2] == "close"):
# 知识库开关指令
# 总结对话开关指令
if not Util.is_admin(e_context):
_set_reply_text("需要管理员权限执行", e_context, level=ReplyType.ERROR)
return
Expand All @@ -192,14 +194,34 @@ def _process_admin_cmd(self, e_context: EventContext):
return

def _is_summary_open(self, context) -> bool:
if not self.sum_config or not self.sum_config.get("enabled"):
return False
if context.kwargs.get("isgroup") and not self.sum_config.get("group_enabled"):
return False
support_type = self.sum_config.get("type") or ["FILE", "SHARING"]
if context.type.name not in support_type and context.type.name != "TEXT":
return False
return True
# 获取远程应用插件状态
remote_enabled = False
if context.kwargs.get("isgroup"):
# 群聊场景只查询群对应的app_code
group_name = context.get("msg").from_user_nickname
app_code = self._fetch_group_app_code(group_name)
if app_code:
remote_enabled = Util.fetch_app_plugin(app_code, "内容总结")
else:
# 非群聊场景使用全局app_code
app_code = conf().get("linkai_app_code")
if app_code:
remote_enabled = Util.fetch_app_plugin(app_code, "内容总结")

# 基础条件:总开关开启且消息类型符合要求
base_enabled = (
self.sum_config
and self.sum_config.get("enabled")
and (context.type.name in (
self.sum_config.get("type") or ["FILE", "SHARING"]) or context.type.name == "TEXT")
)

# 群聊:需要满足(总开关和群开关)或远程插件开启
if context.kwargs.get("isgroup"):
return (base_enabled and self.sum_config.get("group_enabled")) or remote_enabled

# 非群聊:只需要满足总开关或远程插件开启
return base_enabled or remote_enabled

# LinkAI 对话任务处理
def _is_chat_task(self, e_context: EventContext):
Expand Down Expand Up @@ -230,6 +252,19 @@ def _fetch_group_app_code(self, group_name: str) -> str:
app_code = group_mapping.get(group_name) or group_mapping.get("ALL_GROUP")
return app_code

def _fetch_app_code(self, context) -> str:
"""
根据主配置或者群聊名称获取对应的应用code,优先获取群聊配置的应用code
:param context: 上下文
:return: 应用code
"""
app_code = conf().get("linkai_app_code")
if context.kwargs.get("isgroup"):
# 群聊场景只查询群对应的app_code
group_name = context.get("msg").from_user_nickname
app_code = self._fetch_group_app_code(group_name)
return app_code

def get_help_text(self, verbose=False, **kwargs):
trigger_prefix = _get_trigger_prefix()
help_text = "用于集成 LinkAI 提供的知识库、Midjourney绘画、文档总结、联网搜索等能力。\n\n"
Expand Down
29 changes: 25 additions & 4 deletions plugins/linkai/midjourney.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from plugins import EventContext, EventAction
from .utils import Util


INVALID_REQUEST = 410
NOT_FOUND_ORIGIN_IMAGE = 461
NOT_FOUND_TASK = 462
Expand Down Expand Up @@ -67,10 +68,11 @@ def __str__(self):

# midjourney bot
class MJBot:
def __init__(self, config):
def __init__(self, config, fetch_group_app_code):
self.base_url = conf().get("linkai_api_base", "https://api.link-ai.tech") + "/v1/img/midjourney"
self.headers = {"Authorization": "Bearer " + conf().get("linkai_api_key")}
self.config = config
self.fetch_group_app_code = fetch_group_app_code
self.tasks = {}
self.temp_dict = {}
self.tasks_lock = threading.Lock()
Expand Down Expand Up @@ -98,7 +100,7 @@ def judge_mj_task_type(self, e_context: EventContext):
return TaskType.VARIATION
elif cmd_list[0].lower() == f"{trigger_prefix}mjr":
return TaskType.RESET
elif context.type == ContextType.IMAGE_CREATE and self.config.get("use_image_create_prefix") and self.config.get("enabled"):
elif context.type == ContextType.IMAGE_CREATE and self.config.get("use_image_create_prefix") and self._is_mj_open(context):
return TaskType.GENERATE

def process_mj_task(self, mj_type: TaskType, e_context: EventContext):
Expand Down Expand Up @@ -129,8 +131,8 @@ def process_mj_task(self, mj_type: TaskType, e_context: EventContext):
self._set_reply_text(f"Midjourney绘画已{tips_text}", e_context, level=ReplyType.INFO)
return

if not self.config.get("enabled"):
logger.warn("Midjourney绘画未开启,请查看 plugins/linkai/config.json 中的配置")
if not self._is_mj_open(context):
logger.warn("Midjourney绘画未开启,请查看 plugins/linkai/config.json 中的配置,或者在LinkAI平台 应用中添加/打开”MJ“插件")
self._set_reply_text(f"Midjourney绘画未开启", e_context, level=ReplyType.INFO)
return

Expand Down Expand Up @@ -409,6 +411,25 @@ def find_tasks_by_user_id(self, user_id) -> list:
result.append(task)
return result

def _is_mj_open(self, context) -> bool:
# 获取远程应用插件状态
remote_enabled = False
if context.kwargs.get("isgroup"):
# 群聊场景只查询群对应的app_code
group_name = context.get("msg").from_user_nickname
app_code = self.fetch_group_app_code(group_name)
if app_code:
remote_enabled = Util.fetch_app_plugin(app_code, "Midjourney")
else:
# 非群聊场景使用全局app_code
app_code = conf().get("linkai_app_code")
if app_code:
remote_enabled = Util.fetch_app_plugin(app_code, "Midjourney")

# 本地配置
base_enabled = self.config.get("enabled")

return base_enabled or remote_enabled

def _send(channel, reply: Reply, context, retry_cnt=0):
try:
Expand Down
8 changes: 5 additions & 3 deletions plugins/linkai/summary.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,21 @@ class LinkSummary:
def __init__(self):
pass

def summary_file(self, file_path: str):
def summary_file(self, file_path: str, app_code: str):
file_body = {
"file": open(file_path, "rb"),
"name": file_path.split("/")[-1],
"app_code": app_code
}
url = self.base_url() + "/v1/summary/file"
res = requests.post(url, headers=self.headers(), files=file_body, timeout=(5, 300))
return self._parse_summary_res(res)

def summary_url(self, url: str):
def summary_url(self, url: str, app_code: str):
url = html.unescape(url)
body = {
"url": url
"url": url,
"app_code": app_code
}
res = requests.post(url=self.base_url() + "/v1/summary/url", headers=self.headers(), json=body, timeout=(5, 180))
return self._parse_summary_res(res)
Expand Down
21 changes: 20 additions & 1 deletion plugins/linkai/utils.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import requests
from common.log import logger
from config import global_config
from bridge.reply import Reply, ReplyType
from plugins.event import EventContext, EventAction

from config import conf

class Util:
@staticmethod
Expand All @@ -26,3 +28,20 @@ def set_reply_text(content: str, e_context: EventContext, level: ReplyType = Rep
reply = Reply(level, content)
e_context["reply"] = reply
e_context.action = EventAction.BREAK_PASS

@staticmethod
def fetch_app_plugin(app_code: str, plugin_name: str) -> bool:
headers = {"Authorization": "Bearer " + conf().get("linkai_api_key")}
# do http request
base_url = conf().get("linkai_api_base", "https://api.link-ai.tech")
params = {"app_code": app_code}
res = requests.get(url=base_url + "/v1/app/info", params=params, headers=headers, timeout=(5, 10))
if res.status_code == 200:
plugins = res.json().get("data").get("plugins")
for plugin in plugins:
if plugin.get("name") and plugin.get("name") == plugin_name:
return True
return False
else:
logger.warning(f"[LinkAI] find app info exception, res={res}")
return False

0 comments on commit d90eeb7

Please sign in to comment.