nonebot-plugin-aigf 0.1.1__tar.gz → 0.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {nonebot-plugin-aigf-0.1.1 → nonebot-plugin-aigf-0.1.2}/PKG-INFO +1 -1
- {nonebot-plugin-aigf-0.1.1 → nonebot-plugin-aigf-0.1.2}/nonebot_plugin_aigf/__init__.py +9 -9
- nonebot-plugin-aigf-0.1.2/nonebot_plugin_aigf/config.py +37 -0
- {nonebot-plugin-aigf-0.1.1 → nonebot-plugin-aigf-0.1.2}/nonebot_plugin_aigf/image_manager.py +4 -4
- {nonebot-plugin-aigf-0.1.1 → nonebot-plugin-aigf-0.1.2}/nonebot_plugin_aigf/meme_manager.py +1 -1
- {nonebot-plugin-aigf-0.1.1 → nonebot-plugin-aigf-0.1.2}/nonebot_plugin_aigf/session.py +7 -7
- {nonebot-plugin-aigf-0.1.1 → nonebot-plugin-aigf-0.1.2}/nonebot_plugin_aigf.egg-info/PKG-INFO +1 -1
- {nonebot-plugin-aigf-0.1.1 → nonebot-plugin-aigf-0.1.2}/setup.py +1 -1
- nonebot-plugin-aigf-0.1.1/nonebot_plugin_aigf/config.py +0 -37
- {nonebot-plugin-aigf-0.1.1 → nonebot-plugin-aigf-0.1.2}/README.md +0 -0
- {nonebot-plugin-aigf-0.1.1 → nonebot-plugin-aigf-0.1.2}/nonebot_plugin_aigf/client.py +0 -0
- {nonebot-plugin-aigf-0.1.1 → nonebot-plugin-aigf-0.1.2}/nonebot_plugin_aigf/mem.py +0 -0
- {nonebot-plugin-aigf-0.1.1 → nonebot-plugin-aigf-0.1.2}/nonebot_plugin_aigf/memory.py +0 -0
- {nonebot-plugin-aigf-0.1.1 → nonebot-plugin-aigf-0.1.2}/nonebot_plugin_aigf/presets.py +0 -0
- {nonebot-plugin-aigf-0.1.1 → nonebot-plugin-aigf-0.1.2}/nonebot_plugin_aigf/vlm.py +0 -0
- {nonebot-plugin-aigf-0.1.1 → nonebot-plugin-aigf-0.1.2}/nonebot_plugin_aigf.egg-info/SOURCES.txt +0 -0
- {nonebot-plugin-aigf-0.1.1 → nonebot-plugin-aigf-0.1.2}/nonebot_plugin_aigf.egg-info/dependency_links.txt +0 -0
- {nonebot-plugin-aigf-0.1.1 → nonebot-plugin-aigf-0.1.2}/nonebot_plugin_aigf.egg-info/requires.txt +0 -0
- {nonebot-plugin-aigf-0.1.1 → nonebot-plugin-aigf-0.1.2}/nonebot_plugin_aigf.egg-info/top_level.txt +0 -0
- {nonebot-plugin-aigf-0.1.1 → nonebot-plugin-aigf-0.1.2}/setup.cfg +0 -0
|
@@ -47,7 +47,7 @@ from .presets import PRESETS, load_presets, save_preset, RolePreset
|
|
|
47
47
|
from .session import Session
|
|
48
48
|
|
|
49
49
|
__plugin_meta__ = PluginMetadata(
|
|
50
|
-
name="
|
|
50
|
+
name="AI-group-friend", description="群聊特化LLM聊天机器人,具有记忆和表情包功能",
|
|
51
51
|
usage="群聊特化LLM聊天机器人", type="application",
|
|
52
52
|
config=Config, supported_adapters={"~onebot.v11"},
|
|
53
53
|
extra={"author": "shadow3aaa"},
|
|
@@ -69,8 +69,8 @@ class GroupState:
|
|
|
69
69
|
messages_chunk: list[MMessage] = field(default_factory=list)
|
|
70
70
|
last_message_time: float = 0.0
|
|
71
71
|
client: LLMClient = field(default_factory=lambda: LLMClient(
|
|
72
|
-
client=AsyncOpenAI(api_key=plugin_config.
|
|
73
|
-
base_url=plugin_config.
|
|
72
|
+
client=AsyncOpenAI(api_key=plugin_config.aigf_chat_openai_api_key,
|
|
73
|
+
base_url=plugin_config.aigf_chat_openai_base_url)))
|
|
74
74
|
lock = asyncio.Lock()
|
|
75
75
|
|
|
76
76
|
|
|
@@ -197,7 +197,7 @@ async def _(event: GroupMessageEvent, args: Message = CommandArg()):
|
|
|
197
197
|
async def _(event: GroupMessageEvent):
|
|
198
198
|
state = _ensure_group_state(event.group_id)
|
|
199
199
|
state.session = Session(id=str(event.group_id))
|
|
200
|
-
await state.session.load_preset(plugin_config.
|
|
200
|
+
await state.session.load_preset(plugin_config.aigf_default_preset)
|
|
201
201
|
await reset_cmd.finish("已重置会话")
|
|
202
202
|
|
|
203
203
|
|
|
@@ -230,7 +230,7 @@ async def _():
|
|
|
230
230
|
@auto_chat.handle()
|
|
231
231
|
async def handle_auto_chat(bot: Bot, event: GroupMessageEvent):
|
|
232
232
|
group_id = event.group_id
|
|
233
|
-
if group_id not in plugin_config.
|
|
233
|
+
if group_id not in plugin_config.aigf_enabled_groups:
|
|
234
234
|
return
|
|
235
235
|
# 跳过机器人自己的消息
|
|
236
236
|
if event.get_user_id() == str(bot.self_id):
|
|
@@ -289,7 +289,7 @@ async def message2MMessage(bot_name: str, group_id: int, message: Message, bot:
|
|
|
289
289
|
is_sticker = seg.data.get("sub_type") == 1
|
|
290
290
|
image_base64 = base64.b64encode(image_bytes).decode()
|
|
291
291
|
|
|
292
|
-
if plugin_config.
|
|
292
|
+
if plugin_config.aigf_image_mode == "llm":
|
|
293
293
|
await meme_manager.save_to_cache(image_bytes=image_bytes, description="", emotion="")
|
|
294
294
|
else:
|
|
295
295
|
desc = await image_manager.get_image_description(image_base64=image_base64, is_sticker=is_sticker)
|
|
@@ -326,11 +326,11 @@ from nonebot import get_driver
|
|
|
326
326
|
|
|
327
327
|
@get_driver().on_startup
|
|
328
328
|
async def _():
|
|
329
|
-
logger.info(f"[
|
|
329
|
+
logger.info(f"[AI-group-friend] 图片理解模式: {plugin_config.aigf_image_mode}")
|
|
330
330
|
await load_presets()
|
|
331
331
|
await meme_manager.load_all()
|
|
332
332
|
# 加载默认预设到所有群
|
|
333
|
-
default = plugin_config.
|
|
334
|
-
for gid in plugin_config.
|
|
333
|
+
default = plugin_config.aigf_default_preset
|
|
334
|
+
for gid in plugin_config.aigf_enabled_groups:
|
|
335
335
|
state = _ensure_group_state(gid)
|
|
336
336
|
await state.session.load_preset(default)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Copyright (C) 2025 shadow3aaa <shadow3aaaa@gmail.com>
|
|
2
|
+
# Modified by Funny1Potato, 2026
|
|
3
|
+
#
|
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
|
5
|
+
# it under the terms of the GNU Affero General Public License as published by
|
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
|
7
|
+
# (at your option) any later version.
|
|
8
|
+
#
|
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
# GNU Affero General Public License for more details.
|
|
13
|
+
#
|
|
14
|
+
# You should have received a copy of the GNU Affero General Public License
|
|
15
|
+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
16
|
+
|
|
17
|
+
from nonebot import get_driver, get_plugin_config
|
|
18
|
+
from pydantic import BaseModel, Field
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class Config(BaseModel):
|
|
22
|
+
aigf_chat_openai_api_key: str = Field(..., description="LLM API Key")
|
|
23
|
+
aigf_chat_openai_model: str = Field("gpt-3.5-turbo", description="LLM 模型名称")
|
|
24
|
+
aigf_chat_openai_base_url: str = Field("https://api.openai.com/v1", description="LLM API 地址")
|
|
25
|
+
aigf_image_mode: str = Field("vlm", description="图片理解模式: vlm=使用独立VLM, llm=直接用LLM看图")
|
|
26
|
+
aigf_vlm_enabled: bool = Field(True, description="是否启用图片理解(仅 vlm 模式有效)")
|
|
27
|
+
aigf_vlm_model: str = Field("Pro/Qwen/Qwen2.5-VL-7B-Instruct", description="VLM 模型名称")
|
|
28
|
+
aigf_vlm_base_url: str = Field("https://api.siliconflow.cn/v1", description="VLM API 地址")
|
|
29
|
+
aigf_vlm_api_key: str = Field("", description="VLM API Key(为空时使用 chat_openai_api_key)")
|
|
30
|
+
aigf_enabled_groups: list[int] = Field(default_factory=list, description="启用的群号列表")
|
|
31
|
+
aigf_meme_enabled: bool = Field(True, description="是否启用表情包功能")
|
|
32
|
+
aigf_meme_max_count: int = Field(200, description="自动收集的表情包最大数量")
|
|
33
|
+
aigf_default_preset: str = Field("default", description="默认预设名称")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
plugin_config: Config = get_plugin_config(Config)
|
|
37
|
+
global_config = get_driver().config
|
{nonebot-plugin-aigf-0.1.1 → nonebot-plugin-aigf-0.1.2}/nonebot_plugin_aigf/image_manager.py
RENAMED
|
@@ -62,10 +62,10 @@ class ImageManager:
|
|
|
62
62
|
if not self._initialized:
|
|
63
63
|
self._vlm = None
|
|
64
64
|
# 只在 VLM 模式下初始化 VLM
|
|
65
|
-
if plugin_config.
|
|
66
|
-
api_key = plugin_config.
|
|
67
|
-
self._vlm = VLM(api_key=api_key, model=plugin_config.
|
|
68
|
-
base_url=plugin_config.
|
|
65
|
+
if plugin_config.aigf_image_mode == "vlm" and plugin_config.aigf_vlm_enabled:
|
|
66
|
+
api_key = plugin_config.aigf_vlm_api_key or plugin_config.aigf_chat_openai_api_key
|
|
67
|
+
self._vlm = VLM(api_key=api_key, model=plugin_config.aigf_vlm_model,
|
|
68
|
+
base_url=plugin_config.aigf_vlm_base_url)
|
|
69
69
|
IMAGE_CACHE_DIR.mkdir(parents=True, exist_ok=True)
|
|
70
70
|
self._initialized = True
|
|
71
71
|
|
|
@@ -252,7 +252,7 @@ class MemeManager:
|
|
|
252
252
|
async def cleanup(self, max_count: int = 0):
|
|
253
253
|
"""自动收集的表情包超过上限时清理。优先级:最近未使用的先删"""
|
|
254
254
|
if max_count <= 0:
|
|
255
|
-
max_count = plugin_config.
|
|
255
|
+
max_count = plugin_config.aigf_meme_max_count
|
|
256
256
|
if len(self.collected_memes) <= max_count:
|
|
257
257
|
return
|
|
258
258
|
to_remove = []
|
|
@@ -70,7 +70,7 @@ class Session:
|
|
|
70
70
|
|
|
71
71
|
# LLM 模式:读取缓存图片的 base64
|
|
72
72
|
sticker_images: list[str] = []
|
|
73
|
-
if plugin_config.
|
|
73
|
+
if plugin_config.aigf_image_mode == "llm" and cached_stickers:
|
|
74
74
|
for s in cached_stickers:
|
|
75
75
|
cache_info = meme_manager._cache_index.get(s["id"])
|
|
76
76
|
if cache_info:
|
|
@@ -85,7 +85,7 @@ class Session:
|
|
|
85
85
|
|
|
86
86
|
# 调用 LLM
|
|
87
87
|
response_str = await llm.generate_response(
|
|
88
|
-
prompt, plugin_config.
|
|
88
|
+
prompt, plugin_config.aigf_chat_openai_model,
|
|
89
89
|
images=sticker_images if sticker_images else None,
|
|
90
90
|
)
|
|
91
91
|
if not response_str:
|
|
@@ -108,7 +108,7 @@ class Session:
|
|
|
108
108
|
# 处理表情包保存
|
|
109
109
|
save_meme = result.get("memory", {}).get("save_meme")
|
|
110
110
|
|
|
111
|
-
if save_meme and plugin_config.
|
|
111
|
+
if save_meme and plugin_config.aigf_meme_enabled:
|
|
112
112
|
save_list = save_meme if isinstance(save_meme, list) else [save_meme]
|
|
113
113
|
current_cache = meme_manager.get_cached_stickers()
|
|
114
114
|
current_ids = {s["id"] for s in current_cache}
|
|
@@ -155,7 +155,7 @@ class Session:
|
|
|
155
155
|
cached_stickers: list[dict] | None = None) -> str:
|
|
156
156
|
# 预设知识
|
|
157
157
|
preset_knowledge = ""
|
|
158
|
-
preset = PRESETS.get(plugin_config.
|
|
158
|
+
preset = PRESETS.get(plugin_config.aigf_default_preset)
|
|
159
159
|
if preset and preset.knowledges:
|
|
160
160
|
preset_knowledge = "\n".join(f"- {k}" for k in preset.knowledges)
|
|
161
161
|
|
|
@@ -183,7 +183,7 @@ class Session:
|
|
|
183
183
|
|
|
184
184
|
# 表情包列表
|
|
185
185
|
meme_section = ""
|
|
186
|
-
if plugin_config.
|
|
186
|
+
if plugin_config.aigf_meme_enabled:
|
|
187
187
|
meme_list = meme_manager.prompt_list()
|
|
188
188
|
if meme_list:
|
|
189
189
|
meme_section = f"""
|
|
@@ -197,8 +197,8 @@ class Session:
|
|
|
197
197
|
# 缓存中的表情包(可供收藏)
|
|
198
198
|
sticker_section = ""
|
|
199
199
|
|
|
200
|
-
if cached_stickers and plugin_config.
|
|
201
|
-
if plugin_config.
|
|
200
|
+
if cached_stickers and plugin_config.aigf_meme_enabled:
|
|
201
|
+
if plugin_config.aigf_image_mode == "llm":
|
|
202
202
|
# LLM 模式:图片已附在 prompt 中,LLM 直接看图
|
|
203
203
|
sticker_ids = [s['id'] for s in cached_stickers]
|
|
204
204
|
sticker_section = """
|
|
@@ -5,7 +5,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
|
|
|
5
5
|
|
|
6
6
|
setuptools.setup(
|
|
7
7
|
name="nonebot-plugin-aigf", # 用自己的名替换其中的YOUR_USERNAME_
|
|
8
|
-
version="0.1.
|
|
8
|
+
version="0.1.2", #包版本号,便于维护版本
|
|
9
9
|
author="Funny1Potato", #作者,可以写自己的姓名
|
|
10
10
|
author_email="funny_potato@126.com", #作者联系方式,可写自己的邮箱地址
|
|
11
11
|
description="基于nonebot-plugin-nyaturingtest重构的群聊特化 LLM 聊天机器人,具有 LLM 驱动的记忆系统和表情包功能。",#包的简述
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
# Copyright (C) 2025 shadow3aaa <shadow3aaaa@gmail.com>
|
|
2
|
-
# Modified by Funny1Potato, 2026
|
|
3
|
-
#
|
|
4
|
-
# This program is free software: you can redistribute it and/or modify
|
|
5
|
-
# it under the terms of the GNU Affero General Public License as published by
|
|
6
|
-
# the Free Software Foundation, either version 3 of the License, or
|
|
7
|
-
# (at your option) any later version.
|
|
8
|
-
#
|
|
9
|
-
# This program is distributed in the hope that it will be useful,
|
|
10
|
-
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
-
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
-
# GNU Affero General Public License for more details.
|
|
13
|
-
#
|
|
14
|
-
# You should have received a copy of the GNU Affero General Public License
|
|
15
|
-
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
16
|
-
|
|
17
|
-
from nonebot import get_driver, get_plugin_config
|
|
18
|
-
from pydantic import BaseModel, Field
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class Config(BaseModel):
|
|
22
|
-
nyaturingtest_chat_openai_api_key: str = Field(..., description="LLM API Key")
|
|
23
|
-
nyaturingtest_chat_openai_model: str = Field("gpt-3.5-turbo", description="LLM 模型名称")
|
|
24
|
-
nyaturingtest_chat_openai_base_url: str = Field("https://api.openai.com/v1", description="LLM API 地址")
|
|
25
|
-
nyaturingtest_image_mode: str = Field("vlm", description="图片理解模式: vlm=使用独立VLM, llm=直接用LLM看图")
|
|
26
|
-
nyaturingtest_vlm_enabled: bool = Field(True, description="是否启用图片理解(仅 vlm 模式有效)")
|
|
27
|
-
nyaturingtest_vlm_model: str = Field("Pro/Qwen/Qwen2.5-VL-7B-Instruct", description="VLM 模型名称")
|
|
28
|
-
nyaturingtest_vlm_base_url: str = Field("https://api.siliconflow.cn/v1", description="VLM API 地址")
|
|
29
|
-
nyaturingtest_vlm_api_key: str = Field("", description="VLM API Key(为空时使用 chat_openai_api_key)")
|
|
30
|
-
nyaturingtest_enabled_groups: list[int] = Field(default_factory=list, description="启用的群号列表")
|
|
31
|
-
nyaturingtest_meme_enabled: bool = Field(True, description="是否启用表情包功能")
|
|
32
|
-
nyaturingtest_meme_max_count: int = Field(200, description="自动收集的表情包最大数量")
|
|
33
|
-
nyaturingtest_default_preset: str = Field("default", description="默认预设名称")
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
plugin_config: Config = get_plugin_config(Config)
|
|
37
|
-
global_config = get_driver().config
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{nonebot-plugin-aigf-0.1.1 → nonebot-plugin-aigf-0.1.2}/nonebot_plugin_aigf.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{nonebot-plugin-aigf-0.1.1 → nonebot-plugin-aigf-0.1.2}/nonebot_plugin_aigf.egg-info/requires.txt
RENAMED
|
File without changes
|
{nonebot-plugin-aigf-0.1.1 → nonebot-plugin-aigf-0.1.2}/nonebot_plugin_aigf.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|