Files
Mofox-Core/docs/plugins/dependency-management.md
SengokuCola 68f3a9504a add doc
2025-06-20 12:08:15 +08:00

326 lines
9.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 📦 插件依赖管理系统
> 🎯 **简介**MaiBot插件系统提供了强大的Python包依赖管理功能让插件开发更加便捷和可靠。
## ✨ 功能概述
### 🎯 核心能力
- **声明式依赖**插件可以明确声明需要的Python包
- **智能检查**:自动检查依赖包的安装状态
- **版本控制**:精确的版本要求管理
- **可选依赖**:区分必需依赖和可选依赖
- **自动安装**:可选的自动安装功能
- **批量管理**生成统一的requirements文件
- **安全控制**:防止意外安装和版本冲突
### 🔄 工作流程
1. **声明依赖** → 在插件中声明所需的Python包
2. **加载检查** → 插件加载时自动检查依赖状态
3. **状态报告** → 详细报告缺失或版本不匹配的依赖
4. **智能安装** → 可选择自动安装或手动安装
5. **运行时处理** → 插件运行时优雅处理依赖缺失
## 🚀 快速开始
### 步骤1声明依赖
在你的插件类中添加`python_dependencies`字段:
```python
from src.plugin_system import BasePlugin, PythonDependency, register_plugin
@register_plugin
class MyPlugin(BasePlugin):
name = "my_plugin"
# 声明Python包依赖
python_dependencies = [
PythonDependency(
package_name="requests",
version=">=2.25.0",
description="HTTP请求库用于网络通信"
),
PythonDependency(
package_name="numpy",
version=">=1.20.0",
optional=True,
description="数值计算库(可选功能)"
),
]
def get_plugin_components(self):
# 返回插件组件
return []
```
### 步骤2处理依赖
在组件代码中优雅处理依赖缺失:
```python
class MyAction(BaseAction):
async def execute(self, action_input, context=None):
try:
import requests
# 使用requests进行网络请求
response = requests.get("https://api.example.com")
return {"status": "success", "data": response.json()}
except ImportError:
return {
"status": "error",
"message": "功能不可用缺少requests库",
"hint": "请运行: pip install requests>=2.25.0"
}
```
### 步骤3检查和管理
使用依赖管理API
```python
from src.plugin_system import plugin_manager
# 检查所有插件的依赖状态
result = plugin_manager.check_all_dependencies()
print(f"检查了 {result['total_plugins_checked']} 个插件")
print(f"缺少必需依赖的插件: {result['plugins_with_missing_required']} 个")
# 生成requirements文件
plugin_manager.generate_plugin_requirements("plugin_requirements.txt")
```
## 📚 详细教程
### PythonDependency 类详解
`PythonDependency`是依赖声明的核心类:
```python
PythonDependency(
package_name="requests", # 导入时的包名
version=">=2.25.0", # 版本要求
optional=False, # 是否为可选依赖
description="HTTP请求库", # 依赖描述
install_name="" # pip安装时的包名可选
)
```
#### 参数说明
| 参数 | 类型 | 必需 | 说明 |
|------|------|------|------|
| `package_name` | str | ✅ | Python导入时使用的包名`requests` |
| `version` | str | ❌ | 版本要求支持pip格式`>=1.0.0`, `==2.1.3` |
| `optional` | bool | ❌ | 是否为可选依赖,默认`False` |
| `description` | str | ❌ | 依赖的用途描述 |
| `install_name` | str | ❌ | pip安装时的包名默认与`package_name`相同 |
#### 版本格式示例
```python
# 常用版本格式
PythonDependency("requests", ">=2.25.0") # 最小版本
PythonDependency("numpy", ">=1.20.0,<2.0.0") # 版本范围
PythonDependency("pillow", "==8.3.2") # 精确版本
PythonDependency("scipy", ">=1.7.0,!=1.8.0") # 排除特定版本
```
#### 特殊情况处理
**导入名与安装名不同的包:**
```python
PythonDependency(
package_name="PIL", # import PIL
install_name="Pillow", # pip install Pillow
version=">=8.0.0"
)
```
**可选依赖示例:**
```python
python_dependencies = [
# 必需依赖 - 核心功能
PythonDependency(
package_name="requests",
version=">=2.25.0",
description="HTTP库插件核心功能必需"
),
# 可选依赖 - 增强功能
PythonDependency(
package_name="numpy",
version=">=1.20.0",
optional=True,
description="数值计算库,用于高级数学运算"
),
PythonDependency(
package_name="matplotlib",
version=">=3.0.0",
optional=True,
description="绘图库,用于数据可视化功能"
),
]
```
### 依赖检查机制
系统在以下时机会自动检查依赖:
1. **插件加载时**:检查插件声明的所有依赖
2. **手动调用时**通过API主动检查
3. **运行时检查**:在组件执行时动态检查
#### 检查结果状态
| 状态 | 描述 | 处理建议 |
|------|------|----------|
| `no_dependencies` | 插件未声明任何依赖 | 无需处理 |
| `ok` | 所有依赖都已满足 | 正常使用 |
| `missing_optional` | 缺少可选依赖 | 部分功能不可用,考虑安装 |
| `missing_required` | 缺少必需依赖 | 插件功能受限,需要安装 |
## 🎯 最佳实践
### 1. 依赖声明原则
#### ✅ 推荐做法
```python
python_dependencies = [
# 明确的版本要求
PythonDependency(
package_name="requests",
version=">=2.25.0,<3.0.0", # 主版本兼容
description="HTTP请求库用于API调用"
),
# 合理的可选依赖
PythonDependency(
package_name="numpy",
version=">=1.20.0",
optional=True,
description="数值计算库,用于数据处理功能"
),
]
```
#### ❌ 避免的做法
```python
python_dependencies = [
# 过于宽泛的版本要求
PythonDependency("requests"), # 没有版本限制
# 过于严格的版本要求
PythonDependency("numpy", "==1.21.0"), # 精确版本过于严格
# 缺少描述
PythonDependency("matplotlib", ">=3.0.0"), # 没有说明用途
]
```
### 2. 错误处理模式
#### 优雅降级模式
```python
class SmartAction(BaseAction):
async def execute(self, action_input, context=None):
# 检查可选依赖
try:
import numpy as np
# 使用numpy的高级功能
return await self._advanced_processing(action_input, np)
except ImportError:
# 降级到基础功能
return await self._basic_processing(action_input)
async def _advanced_processing(self, input_data, np):
"""使用numpy的高级处理"""
result = np.array(input_data).mean()
return {"result": result, "method": "advanced"}
async def _basic_processing(self, input_data):
"""基础处理(不依赖外部库)"""
result = sum(input_data) / len(input_data)
return {"result": result, "method": "basic"}
```
## 🔧 使用API
### 检查依赖状态
```python
from src.plugin_system import plugin_manager
# 检查所有插件依赖(仅检查,不安装)
result = plugin_manager.check_all_dependencies(auto_install=False)
# 检查并自动安装缺失的必需依赖
result = plugin_manager.check_all_dependencies(auto_install=True)
```
### 生成requirements文件
```python
# 生成包含所有插件依赖的requirements文件
plugin_manager.generate_plugin_requirements("plugin_requirements.txt")
```
### 获取依赖状态报告
```python
# 获取详细的依赖检查报告
result = plugin_manager.check_all_dependencies()
for plugin_name, status in result['plugin_status'].items():
print(f"插件 {plugin_name}: {status['status']}")
if status['missing']:
print(f" 缺失必需依赖: {status['missing']}")
if status['optional_missing']:
print(f" 缺失可选依赖: {status['optional_missing']}")
```
## 🛡️ 安全考虑
### 1. 自动安装控制
- 🛡️ **默认手动**: 自动安装默认关闭,需要明确启用
- 🔍 **依赖审查**: 安装前会显示将要安装的包列表
- ⏱️ **超时控制**: 安装操作有超时限制5分钟
### 2. 权限管理
- 📁 **环境隔离**: 推荐在虚拟环境中使用
- 🔒 **版本锁定**: 支持精确的版本控制
- 📝 **安装日志**: 记录所有安装操作
## 📊 故障排除
### 常见问题
1. **依赖检查失败**
```python
# 手动检查包是否可导入
try:
import package_name
print("包可用")
except ImportError:
print("包不可用,需要安装")
```
2. **版本冲突**
```python
# 检查已安装的包版本
import package_name
print(f"当前版本: {package_name.__version__}")
```
3. **安装失败**
```python
# 查看安装日志
from src.plugin_system import dependency_manager
result = dependency_manager.get_install_summary()
print("安装日志:", result['install_log'])
print("失败详情:", result['failed_installs'])
```