Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Summary and MJ support can be configured through LinkAI platform app plugins #2363

Merged
merged 1 commit into from
Oct 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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