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

完善插件管理器的消息处理机制并启用多个插件 #1382

Open
wants to merge 34 commits into
base: refactoring-v3-mvp
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
a57767c
fix(ratelimit): 对齐 current_day
lss233 Sep 8, 2023
ea795b1
fix(dependencies): add creart
lss233 Sep 10, 2023
b82ead5
Update requirements.txt
lss233 Sep 10, 2023
e98d2a1
fix: requirements
lss233 Sep 13, 2023
a4dfc6e
Update requirements.txt
magisk317 Sep 9, 2023
546f248
Replace deprecated FreeTypeFont.getsize()
xslingcn Oct 14, 2023
d61e547
完善HTTP API说明
liu2-3zhi Feb 5, 2024
7eb7061
修复BUG
liu2-3zhi Feb 13, 2024
42c0a82
Update http_service.py
liu2-3zhi Feb 14, 2024
706cc51
Update http_service.py
liu2-3zhi Feb 14, 2024
3cab527
Update http_service.py
liu2-3zhi Feb 14, 2024
6b1307c
暂时抑制用户发送空消息时关于未定义conversation_handler的报错(转至log报错)
TNTcraftHIM Feb 12, 2024
6d55731
修改log输出至stdout
TNTcraftHIM Feb 13, 2024
d1f9c37
可以在preset的system字段中添加{date}变量
TNTcraftHIM Feb 14, 2024
7a27f4a
在重置会话时自动加载默认预设
TNTcraftHIM Feb 15, 2024
0aa5b69
允许在sdwebui配置中添加alwayson_scripts
TNTcraftHIM Feb 16, 2024
3f66e7b
Merge remote-tracking branch 'upstream/refactoring-v3-mvp' into refac…
Jan 3, 2025
2aa7bed
主要更新:
Jan 3, 2025
431dd76
更新配置文件结构并新增多个默认插件(openai插件,自动工作流插件,提示词增强插件,图片生成插件,音乐插件,天气插件)
Jan 3, 2025
7538d29
更新自动工作流插件,加入重试机制
Jan 3, 2025
68b2bb5
生成图片插件漏了配置文件
Jan 3, 2025
3e3fc39
回滚实例注册,采用已有的实例管理
Jan 4, 2025
6b20fe5
自动工作流插件,改用已有的llm实例进行访问
Jan 4, 2025
bf7063b
新增定时器插件
Jan 4, 2025
0c2b80b
定时任务插件:优化一次性定时任务的load
Jan 4, 2025
244957b
新增图片理解插件。
Jan 5, 2025
7ed0cc3
去除多余的实例加载
Jan 5, 2025
85c494f
Merge remote-tracking branch 'refs/remotes/upstream/refactoring-v3-mv…
Jan 6, 2025
dfd1398
冲突
Jan 6, 2025
de612d4
主代码更新IMMessage
Jan 6, 2025
6785a45
主代码更新IMMessage
Jan 6, 2025
6ed61ec
定时任务根据chat_id存储
Jan 7, 2025
8d5dd02
增加快捷回复的触发规则
Jan 8, 2025
6dd7d2d
优化插件-工作流触发
Jan 10, 2025
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
54 changes: 48 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -214,8 +214,8 @@ debug = false
|:---|:---|:---|
|result| String |SUCESS,DONE,FAILED|
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

问题(拼写错误): 拼写错误:SUCESS 应该是 SUCCESS。

Suggested change
|result| String |SUCESS,DONE,FAILED|
|result| String |SUCCESS,DONE,FAILED|
Original comment in English

issue (typo): Typo: SUCESS should be SUCCESS.

Suggested change
|result| String |SUCESS,DONE,FAILED|
|result| String |SUCCESS,DONE,FAILED|

|message| String[] |文本返回,支持多段返回|
|voice| String[] |音频返回,支持多个音频的base64编码;参考:data:audio/mpeg;base64,...|
|image| String[] |图片返回,支持多个图片的base64编码;参考:data:image/png;base64,...|
|voice| String[] |音频返回,支持多个音频的base64编码;参考:data:audio/mpeg;base64,,iVBORw0KGgoAAAANS...|
|image| String[] |图片返回,支持多个图片的base64编码;参考:data:image/png;base64,UhEUgAAAgAAAAIACAIA...|
Comment on lines +217 to +218
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

建议: Base64示例似乎被截断。

Base64的语音和图像示例似乎被截断。请完成这些示例或指明它们是占位符。

Suggested change
|voice| String[] |音频返回,支持多个音频的base64编码;参考:data:audio/mpeg;base64,,iVBORw0KGgoAAAANS...|
|image| String[] |图片返回,支持多个图片的base64编码;参考:data:image/png;base64,UhEUgAAAgAAAAIACAIA...|
|voice| String[] |音频返回,支持多个音频的base64编码;参考:data:audio/mpeg;base64,<base64_encoded_audio_data>|
|image| String[] |图片返回,支持多个图片的base64编码;参考:data:image/png;base64,<base64_encoded_image_data>|
Original comment in English

suggestion: Base64 examples seem truncated.

The base64 examples for voice and image appear truncated. Please complete these examples or indicate that they are placeholders.

Suggested change
|voice| String[] |音频返回,支持多个音频的base64编码;参考:data:audio/mpeg;base64,,iVBORw0KGgoAAAANS...|
|image| String[] |图片返回,支持多个图片的base64编码;参考:data:image/png;base64,UhEUgAAAgAAAAIACAIA...|
|voice| String[] |音频返回,支持多个音频的base64编码;参考:data:audio/mpeg;base64,<base64_encoded_audio_data>|
|image| String[] |图片返回,支持多个图片的base64编码;参考:data:image/png;base64,<base64_encoded_image_data>|


**响应示例**
```json
Expand Down Expand Up @@ -245,6 +245,17 @@ debug = false
"message": "ping"
}
```

* 请注意,`session_id`请采用规范格式。其格式为`friend-`(好友)或`group-`(群组)加字符串

示例
```
friend-R6sxRvblulTZqNC
group-M3jpvxv26mKVM
```

如果不能正确继续是好友还是群组,将一律按照群组处理

**响应格式**
字符串:request_id

Expand All @@ -253,6 +264,12 @@ debug = false
1681525479905
```

* 请注意,返回的内容可能会带有引号。请去除引号。(包括 `"` 和 `'` )

```
'1681525479905'
```

**GET** `/v2/chat/response`

**请求参数**
Expand All @@ -265,13 +282,28 @@ debug = false
```
/v2/chat/response?request_id=1681525479905
```
* 请注意,request_id不能带有引号(包括 `"` 和 `'` )。
下列为错误示范
```
/v2/chat/response?request_id='1681525479905'
```
```
/v2/chat/response?request_id="1681525479905"
```
```
/v2/chat/response?request_id='1681525479905"
```
```
/v2/chat/response?request_id="1681525479905'
```

**响应格式**
|参数名|类型|说明|
|:---|:---|:---|
|result| String |SUCESS,DONE,FAILED|
|message| String[] |文本返回,支持多段返回|
|voice| String[] |音频返回,支持多个音频的base64编码;参考:data:audio/mpeg;base64,...|
|image| String[] |图片返回,支持多个图片的base64编码;参考:data:image/png;base64,...|
|voice| String[] |音频返回,支持多个音频的base64编码;参考:data:audio/mpeg;base64,,iVBORw0KGgoAAAANS...|
|image| String[] |图片返回,支持多个图片的base64编码;参考:data:image/png;base64,UhEUgAAAgAAAAIACAIA...|

* 每次请求返回增量并清空。DONE、FAILED之后没有更多返回。

Expand All @@ -280,10 +312,20 @@ debug = false
{
"result": "DONE",
"message": ["pong!"],
"voice": ["data:audio/mpeg;base64,..."],
"image": ["data:image/png;base64,...", "data:image/png;base64,..."]
"voice": ["data:audio/mpeg;base64,iVBORw0KGgoAAAANS..."],
"image": ["data:image/png;base64,UhEUgAAAgAAAAIACAIA...", "data:image/png;base64,UhEUgAAAgAAAAIACAIA..."]
}
```
* 请注意,当返回 `SUCCESS`的时候表示等待
```json
{"result": "SUCCESS", "message": [], "voice": [], "image": []}
```
* 请注意,可能有多条`DONE`,请一直请求,直到出现`FAILED`。`FAILED`表示回复完毕。
```json
{"result": "FAILED", "message": ["\u6ca1\u6709\u66f4\u591a\u4e86\uff01"], "voice": [], "image": []}
```
* 请注意`DONE`和`FAILED`之间可能会穿插`SUCCESS`。整个回复周期可能会大于一分钟。

</details>

## 🦊 加载预设
Expand Down
39 changes: 39 additions & 0 deletions config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
ims:
configs:
onebot-default:
access_token: ''
filter_file: filter.json
heartbeat_interval: '15000'
host: 127.0.0.1
name: onebot
port: '8567'
reconnect_interval: '3000'
enable:
onebot:
- onebot-default
llms:
backends:
openai:
adapter: openai
configs:
- api_base: https://wind.chuansir.top/v1
api_key: d3105c0f-f739-443d-922d-f937d2ee6ab6
model: claude-3.5-sonnet
- api_base: https://api.deepseek.com
api_key: sk-dc067d626bca4feaaf2bc7e4ed6c965b
model: deepseek-chat
enable: true
models:
- claude-3.5-sonnet
- deepseek-chat
plugins:
enable:
- image_generator
- image_understanding
- music_player
- onebot_adapter
- openai_adapter
- prompt_generator
- scheduler_plugin
- weather_query
- workflow_plugin
14 changes: 13 additions & 1 deletion config.yaml.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,17 @@ ims:
telegram-bot-1234:
token: 'abcd'

llms:
backends: # 这是必需的
openai: # 后端名称作为key
enable: true
adapter: "openai"
configs:
- api_key: ""
api_base: "https://wind.chuansir.top/v1" # 可选
model: "claude-3.5-sonnet" # 可选,也可以在调用时指定
models:
- "claude-3.5-sonnet"

plugins:
enable: []
enable: ['onebot_adapter','openai_adapter','workflow_plugin','prompt_generator','music_player','weather_query','scheduler_plugin']
8 changes: 7 additions & 1 deletion framework/config/global_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,13 @@ class LLMBackendConfig(BaseModel):

class LLMConfig(BaseModel):
backends: Dict[str, LLMBackendConfig]


class PluginsConfig(BaseModel):
"""插件配置"""
enable: List[str] = [] # 启用的插件列表

class GlobalConfig(BaseModel):
"""全局配置"""
ims: IMConfig
llms: LLMConfig
plugins: PluginsConfig = PluginsConfig() # 插件配置
16 changes: 8 additions & 8 deletions framework/im/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ class IMManager:
IM 生命周期管理器,负责管理所有 adapter 的启动、运行和停止。
"""
container: DependencyContainer

config: GlobalConfig

im_registry: IMRegistry

@Inject()
def __init__(self, container: DependencyContainer, config: GlobalConfig, adapter_registry: IMRegistry):
self.container = container
Expand All @@ -33,7 +33,7 @@ def start_adapters(self, loop=None):
"""
if loop is None:
loop = asyncio.get_event_loop()

enable_ims = self.config.ims.enable
credentials = self.config.ims.configs

Expand Down Expand Up @@ -66,18 +66,18 @@ def stop_adapters(self, loop=None):
"""
if loop is None:
loop = asyncio.get_event_loop()

for key, adapter in self.adapters.items():
asyncio.ensure_future(self._stop_adapter(key, adapter, loop), loop=loop)


def get_adapters(self) -> Dict[str, any]:
"""
获取所有已启动的 adapter。
:return: 已启动的 adapter 字典。
"""
return self.adapters

async def _start_adapter(self, key, adapter, loop):
logger.info(f"Starting adapter: {key}")
await adapter.start()
Expand All @@ -86,4 +86,4 @@ async def _start_adapter(self, key, adapter, loop):
async def _stop_adapter(self, key, adapter, loop):
logger.info(f"Stopping adapter: {key}")
await adapter.stop()
logger.info(f"Stopped adapter: {key}")
logger.info(f"Stopped adapter: {key}")
3 changes: 2 additions & 1 deletion framework/llm/format/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ class LLMChatRequest(BaseModel):
stream_options: Optional[Any] = None
temperature: Optional[int] = None
top_p: Optional[int] = None
top_k: Optional[int] = None
tools: Optional[Any] = None
tool_choice: Optional[str] = None
logprobs: Optional[bool] = None
top_logprobs: Optional[Any] = None
top_logprobs: Optional[Any] = None
3 changes: 2 additions & 1 deletion framework/llm/format/response.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class LLMChatResponseContent(BaseModel):
logprobs: Optional[Logprobs] = None

class LLMChatResponse(BaseModel):
raw_message : str = None
content: Optional[List[LLMChatResponseContent]] = None
model: Optional[str] = None
usage: Optional[Usage] = None
usage: Optional[Usage] = None
24 changes: 12 additions & 12 deletions framework/llm/llm_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,48 +11,48 @@ class LLMManager:
跟踪、管理和调度模型后端
"""
container: DependencyContainer

config: GlobalConfig

backend_registry: LLMBackendRegistry

active_backends: Dict[str, List[LLMBackendAdapter]]

@Inject()
def __init__(self, container: DependencyContainer, config: GlobalConfig, backend_registry: LLMBackendRegistry):
self.container = container
self.config = config
self.backend_registry = backend_registry
self.logger = get_logger("LLMAdapter")
self.active_backends = {}

def load_config(self):
for key, backend_config in self.config.llms.backends.items():
if backend_config.enable:
self.logger.info(f"Loading backend: {key}")
self.load_backend(key, backend_config)

def load_backend(self, name: str, backend_config: LLMBackendConfig):
if name in self.active_backends:
raise ValueError

adapter_class = self.backend_registry.get(backend_config.adapter)
config_class = self.backend_registry.get_config_class(backend_config.adapter)

if not adapter_class or not config_class:
raise ValueError

configs = [config_class(**config_entry) for config_entry in backend_config.configs]

adapters = []

for config in configs:
with self.container.scoped() as scoped_container:
scoped_container.register(config_class, config)
adapter = Inject(scoped_container).create(adapter_class)()
adapters.append(adapter)
self.logger.info(f"Loaded {len(adapters)} adapters for backend: {name}")
self.active_backends[name] = adapters

def get_llm(self, model_id: str) -> Optional[LLMBackendAdapter]:
pass
pass
6 changes: 3 additions & 3 deletions framework/llm/llm_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class LLMAbility(Enum):
ImageGeneration = ImageInput | ImageOutput
TextImageMultiModal = Chat | ImageGeneration
TextImageAudioMultiModal = TextImageMultiModal | AudioInput | AudioOutput

class LLMBackendRegistry:
"""
LLM 注册表,用于动态注册和管理 LLM 适配器及其配置。
Expand Down Expand Up @@ -81,7 +81,7 @@ def get_config_class(self, name: str) -> Type[BaseModel]:
if name not in self._config_registry:
raise ValueError(f"Config class for LLMAdapter '{name}' is not registered.")
return self._config_registry[name]

def get_ability(self, name: str) -> LLMAbility:
"""
获取已注册的 LLM 适配器能力。
Expand All @@ -90,4 +90,4 @@ def get_ability(self, name: str) -> LLMAbility:
"""
if name not in self._ability_registry:
raise ValueError(f"LLMAdapter with name '{name}' is not registered.")
return self._ability_registry[name]
return self._ability_registry[name]
43 changes: 40 additions & 3 deletions framework/plugin_manager/plugin.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,29 @@
from abc import ABC, abstractmethod

from typing import Dict, Any, List, Optional, Union, Pattern
from framework.config.global_config import GlobalConfig
from framework.im.im_registry import IMRegistry
from framework.im.manager import IMManager
from framework.ioc.inject import Inject
from framework.llm.llm_registry import LLMBackendRegistry
from framework.llm.llm_manager import LLMManager
from framework.plugin_manager.plugin_event_bus import PluginEventBus
from framework.workflow_dispatcher.workflow_dispatcher import WorkflowDispatcher
from framework.ioc.container import DependencyContainer

class Plugin(ABC):
event_bus: PluginEventBus
workflow_dispatcher: WorkflowDispatcher
llm_registry: LLMBackendRegistry
im_registry: IMRegistry
im_manager: IMManager

llm_manager: LLMManager
config: GlobalConfig
container: DependencyContainer

@Inject()
def __init__(self, config: GlobalConfig = None):
self.config = config

@abstractmethod
def on_load(self):
pass
Expand All @@ -24,4 +34,31 @@ def on_start(self):

@abstractmethod
def on_stop(self):
pass
pass

def get_action_params(self, action: str) -> Dict[str, Any]:
"""获取动作所需的参数描述"""
return {}

async def execute(self, chat_id: str, action: str, params: Dict[str, Any]) -> Dict[str, Any]:
"""执行插件动作"""
return {}

def get_actions(self) -> List[str]:
"""获取插件支持的所有动作"""
return []

def get_action_trigger(self, message: str) -> Optional[Dict[str, Any]]:
"""根据消息内容获取触发的动作和参数

Args:
message: 用户消息内容

Returns:
None: 不触发任何动作
Dict: {
"action": str, # 触发的动作名称
"params": Dict[str, Any] # 动作的参数
}
"""
return None # 默认不触发
Loading