Skip to content

Commit 350d665

Browse files
committed
feat: comprehensive web management system with plugin and system status APIs
- Added new web API modules for system and plugin management - Implemented system status API with resource usage tracking - Created plugin management API with support for: * Plugin listing * Plugin details retrieval * Plugin update (with restrictions) - Enhanced web framework with modular API design - Added setup files for internal plugins - Introduced new web API tests for system, plugin, and workflow management - Improved authentication and authorization mechanisms - Updated configuration models to support new plugin and system features
1 parent 108ed3f commit 350d665

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+2327
-530
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ Dockerfile.dev
1010
venv/
1111
.vscode/
1212
config.yaml
13+
config.yaml.bak
1314
logs/

config.yaml.example

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,36 @@
11
ims:
2-
enable:
3-
telegram: ['telegram-bot-1234']
4-
configs:
5-
telegram-bot-1234:
6-
token: 'abcd'
2+
- name: "telegram-bot-1234"
3+
enable: true
4+
adapter: "telegram"
5+
config:
6+
token: "abcd"
77

88
plugins:
99
enable: []
1010

1111
web:
1212
host: "127.0.0.1"
1313
port: 8080
14-
secret_key: "please-change-this-to-a-secure-secret-key"
14+
secret_key: "please-change-this-to-a-secure-secret-key"
15+
16+
llms:
17+
api_backends:
18+
- name: "deepseek-official"
19+
adapter: "deepseek"
20+
enable: true
21+
config:
22+
api_key: "your-api-key"
23+
base_url: "https://api.deepseek.com/v1"
24+
models:
25+
- "deepseek-chat"
26+
- "deepseek-coder"
27+
28+
- name: "openai-gpt4"
29+
adapter: "openai"
30+
enable: true
31+
config:
32+
api_key: "your-openai-key"
33+
base_url: "https://api.openai.com/v1"
34+
models:
35+
- "gpt-4"
36+
- "gpt-4-turbo"
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
name: Test Workflow
2+
blocks:
3+
- type: test:message
4+
name: Message Node
5+
params: {}

framework/config/global_config.py

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,25 @@
22

33
from pydantic import BaseModel, Field
44

5+
56
class IMConfig(BaseModel):
6-
enable: Dict[str, List[str]] = dict()
7-
configs: Dict[str, Dict[str, Any]] = Field(description="IM 的凭证配置", default=dict())
7+
"""IM配置"""
8+
name: str = Field(default="", description="IM标识名称")
9+
enable: bool = Field(default=True, description="是否启用IM")
10+
adapter: str = Field(default="dummy", description="IM适配器类型")
11+
config: Dict[str, Any] = Field(default={}, description="IM的配置")
812

913
class LLMBackendConfig(BaseModel):
10-
enable: bool
11-
adapter: str
12-
configs: List[Dict]
13-
models: List[str]
14+
"""LLM后端配置"""
15+
name: str = Field(description="后端标识名称")
16+
adapter: str = Field(description="LLM适配器类型")
17+
config: Dict[str, Any] = Field(default={}, description="后端配置")
18+
enable: bool = Field(default=True, description="是否启用")
19+
models: List[str] = Field(default=[], description="支持的模型列表")
1420

1521
class LLMConfig(BaseModel):
16-
backends: Dict[str, LLMBackendConfig] = dict()
17-
22+
api_backends: List[LLMBackendConfig] = Field(default=[], description="LLM API后端列表")
23+
1824
class DefaultConfig(BaseModel):
1925
llm_model: str = Field(default="gemini-1.5-flash", description="默认使用的 LLM 模型名称")
2026

@@ -46,9 +52,14 @@ class WebConfig(BaseModel):
4652
secret_key: str = Field(default="", description="Web服务的密钥,用于JWT等加密")
4753
password_file: str = Field(default="./data/web/password.hash", description="密码哈希存储路径")
4854

55+
class PluginConfig(BaseModel):
56+
"""插件配置"""
57+
enable: List[str] = Field(default=[], description="启用的外部插件列表")
58+
4959
class GlobalConfig(BaseModel):
50-
ims: IMConfig = IMConfig()
60+
ims: List[IMConfig] = Field(default=[], description="IM配置列表")
5161
llms: LLMConfig = LLMConfig()
5262
defaults: DefaultConfig = DefaultConfig()
5363
memory: MemoryConfig = MemoryConfig()
5464
web: WebConfig = WebConfig()
65+
plugins: PluginConfig = PluginConfig()

framework/im/im_registry.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,12 @@ def get_config_class(self, name: str) -> Type[BaseModel]:
4141
"""
4242
if name not in self._config_registry:
4343
raise ValueError(f"Config class for adapter '{name}' is not registered.")
44-
return self._config_registry[name]
44+
return self._config_registry[name]
45+
46+
def get_all_adapters(self) -> Dict[str, Type[IMAdapter]]:
47+
"""
48+
获取所有已注册的 adapter。
49+
:return: 所有已注册的 adapter。
50+
"""
51+
return self._registry
52+

framework/im/manager.py

Lines changed: 88 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import asyncio
22
from typing import Dict
33

4-
from framework.config.global_config import GlobalConfig
4+
from framework.config.global_config import GlobalConfig, IMConfig
55
from framework.im.adapter import IMAdapter
66
from framework.im.im_registry import IMRegistry
77
from framework.ioc.container import DependencyContainer
@@ -26,40 +26,76 @@ def __init__(self, container: DependencyContainer, config: GlobalConfig, adapter
2626
self.im_registry = adapter_registry
2727
self.adapters: Dict[str, any] = {}
2828

29+
def get_adapter_type(self, name: str) -> str:
30+
"""
31+
获取指定名称的 adapter 类型。
32+
:param name: adapter 的名称
33+
:return: adapter 的类型
34+
"""
35+
return self.get_adapter_config(name).adapter
36+
37+
def has_adapter(self, name: str) -> bool:
38+
"""
39+
检查指定名称的 adapter 是否存在。
40+
:param name: adapter 的名称
41+
:return: 如果 adapter 存在返回 True,否则返回 False
42+
"""
43+
return name in self.adapters
44+
45+
def get_adapter_config(self, name: str) -> IMConfig:
46+
"""
47+
获取指定名称的 adapter 的配置。
48+
:param name: adapter 的名称
49+
:return: adapter 的配置
50+
"""
51+
for im in self.config.ims:
52+
if im.name == name:
53+
return im
54+
raise ValueError(f"Adapter {name} not found")
55+
56+
def update_adapter_config(self, name: str, config: IMConfig):
57+
"""
58+
更新指定名称的 adapter 的配置。
59+
:param name: adapter 的名称
60+
:param config: adapter 的配置
61+
"""
62+
self.get_adapter_config(name).config = config
63+
64+
def delete_adapter(self, name: str):
65+
"""
66+
删除指定名称的 adapter。
67+
:param name: adapter 的名称
68+
"""
69+
self.adapters.pop(name)
70+
self.config.ims = [im for im in self.config.ims if im.name != name]
2971

3072
def start_adapters(self, loop=None):
3173
"""
3274
根据配置文件中的 enable_ims 启动对应的 adapter。
3375
:param loop: 负责执行的 event loop
3476
"""
35-
if loop is None:
36-
loop = asyncio.get_event_loop()
37-
38-
enable_ims = self.config.ims.enable
39-
credentials = self.config.ims.configs
4077

41-
for platform, adapter_keys in enable_ims.items():
78+
79+
if loop is None:
80+
loop = asyncio.new_event_loop()
81+
tasks = []
82+
for im in self.config.ims:
4283
# 动态获取 adapter 类
43-
adapter_class = self.im_registry.get(platform)
84+
adapter_class = self.im_registry.get(im.adapter)
4485
# 动态获取 adapter 的配置类
45-
config_class = self.im_registry.get_config_class(platform)
46-
tasks = []
47-
for key in adapter_keys:
48-
# 从 credentials 中读取配置
49-
if key not in credentials:
50-
raise ValueError(f"Credential for key '{key}' is missing in credentials.")
51-
credential = credentials[key]
86+
config_class = self.im_registry.get_config_class(im.adapter)
87+
# 动态实例化 adapter 的配置对象
88+
adapter_config = config_class(**im.config)
5289

53-
# 动态实例化 adapter 的配置对象
54-
adapter_config = config_class(**credential)
90+
# 创建 adapter 实例
91+
with self.container.scoped() as scoped_container:
92+
scoped_container.register(config_class, adapter_config)
93+
adapter = Inject(scoped_container).create(adapter_class)()
94+
self.adapters[im.name] = adapter
95+
if im.enable:
96+
tasks.append(asyncio.ensure_future(self._start_adapter(im.name, adapter), loop=loop))
97+
loop.run_until_complete(asyncio.gather(*tasks, return_exceptions=True))
5598

56-
# 创建 adapter 实例
57-
with self.container.scoped() as scoped_container:
58-
scoped_container.register(config_class, adapter_config)
59-
adapter = Inject(scoped_container).create(adapter_class)()
60-
self.adapters[key] = adapter
61-
tasks.append(asyncio.ensure_future(self._start_adapter(key, adapter, loop), loop=loop))
62-
loop.run_until_complete(asyncio.gather(*tasks, return_exceptions=True))
6399

64100
def stop_adapters(self, loop=None):
65101
"""
@@ -70,9 +106,8 @@ def stop_adapters(self, loop=None):
70106
loop = asyncio.get_event_loop()
71107

72108
for key, adapter in self.adapters.items():
73-
loop.run_until_complete(self._stop_adapter(key, adapter, loop))
109+
loop.run_until_complete(self._stop_adapter(key, adapter))
74110

75-
76111
def get_adapters(self) -> Dict[str, any]:
77112
"""
78113
获取所有已启动的 adapter。
@@ -88,12 +123,36 @@ def get_adapter(self, key: str) -> IMAdapter:
88123
"""
89124
return self.adapters[key]
90125

91-
async def _start_adapter(self, key, adapter, loop):
126+
async def _start_adapter(self, key: str, adapter: IMAdapter):
92127
logger.info(f"Starting adapter: {key}")
128+
adapter.is_running = True
93129
await adapter.start()
94130
logger.info(f"Started adapter: {key}")
95131

96-
async def _stop_adapter(self, key, adapter, loop):
132+
async def _stop_adapter(self, key: str, adapter: IMAdapter):
97133
logger.info(f"Stopping adapter: {key}")
134+
adapter.is_running = False
98135
await adapter.stop()
99-
logger.info(f"Stopped adapter: {key}")
136+
logger.info(f"Stopped adapter: {key}")
137+
138+
def stop_adapter(self, adapter_id: str, loop: asyncio.AbstractEventLoop):
139+
if adapter_id not in self.adapters:
140+
raise ValueError(f"Adapter {adapter_id} not found")
141+
adapter = self.adapters[adapter_id]
142+
return asyncio.ensure_future(self._stop_adapter(adapter_id, adapter), loop=loop)
143+
144+
def start_adapter(self, adapter_id: str, loop: asyncio.AbstractEventLoop):
145+
if adapter_id not in self.adapters:
146+
raise ValueError(f"Adapter {adapter_id} not found")
147+
adapter = self.adapters[adapter_id]
148+
return asyncio.ensure_future(self._start_adapter(adapter_id, adapter), loop=loop)
149+
150+
def is_adapter_running(self, key: str) -> bool:
151+
"""
152+
检查指定 key 的 adapter 是否正在运行。
153+
:param key: adapter 的 key
154+
:return: 如果 adapter 正在运行返回 True,否则返回 False
155+
"""
156+
157+
return key in self.adapters and getattr(self.adapters[key], "is_running", False)
158+

0 commit comments

Comments
 (0)