一个简化 Python 插件开发的通用框架
Project description
Python Plugin Framework
一个简化 Python 插件开发的通用框架,让你专注于业务逻辑而不是 gRPC 样板代码。
特性
- ✅ 简单易用:只需实现 2 个核心方法即可创建插件
- 🔄 自动处理:gRPC 通信、日志、错误处理、上下文提取
- 📊 内置追踪:自动提取 W3C Trace Context 和自定义 metadata
- 🎯 类型安全:完整的类型提示支持
- 📝 Go glog 兼容:使用 glog-python,与 Go 服务日志格式一致
- 🔌 可扩展:提供多个可选的钩子方法
快速开始
1. 创建你的插件
#!/usr/bin/env python3
from typing import Dict, Any, Iterator
from python_plugin_framework.core.base_plugin import BasePluginService, serve_plugin
class MyPlugin(BasePluginService):
def __init__(self):
super().__init__(plugin_name="MyPlugin")
def get_plugin_metadata(self) -> Dict[str, Any]:
"""定义插件元数据和参数"""
return {
"kind": "my_plugin",
"node_type": "Node",
"description": "My awesome plugin",
"version": "1.0.0",
"parameters": [
{
"name": "input_text",
"type": "string",
"description": "Input text",
"required": True,
"default_value": ""
}
]
}
def execute(
self,
parameters: Dict[str, Any],
parent_output: Dict[str, Any],
global_vars: Dict[str, Any],
context: Dict[str, Any]
) -> Iterator[Dict[str, Any]]:
"""执行插件逻辑"""
# 获取参数
text = parameters.get("input_text", "")
# 发送日志
yield {"type": "log", "message": f"Processing: {text}"}
# 执行业务逻辑
result = text.upper()
# 返回结果
yield {
"type": "result",
"data": {
"output": result,
"length": len(result)
}
}
if __name__ == "__main__":
import sys
port = int(sys.argv[1]) if len(sys.argv) > 1 else 50052
plugin = MyPlugin()
serve_plugin(plugin, port)
2. 运行插件
python my_plugin.py 50052
核心概念
必须实现的方法
1. get_plugin_metadata()
定义插件的元数据和参数:
def get_plugin_metadata(self) -> Dict[str, Any]:
return {
"kind": "plugin_type", # 插件类型标识
"node_type": "Node", # 节点类型
"description": "Plugin desc", # 描述
"version": "1.0.0", # 版本号
"credential_type": "", # 可选:凭证类型
"parameters": [ # 参数定义
{
"name": "param_name",
"type": "string", # string, int, double, bool
"description": "Param description",
"required": True,
"default_value": "default"
}
]
}
2. execute()
执行插件的核心逻辑(生成器函数):
def execute(
self,
parameters: Dict[str, Any], # 节点参数
parent_output: Dict[str, Any], # 父节点输出
global_vars: Dict[str, Any], # 全局变量
context: Dict[str, Any] # 上下文(trace_id, node_name 等)
) -> Iterator[Dict[str, Any]]:
# 发送日志消息
yield {"type": "log", "message": "Processing..."}
# 返回结果
yield {
"type": "result",
"data": {"key": "value"},
"branch_index": 0 # 可选,默认 0
}
# 或返回错误
yield {"type": "error", "message": "Something went wrong"}
可选方法(可以覆盖)
1. health_check()
自定义健康检查:
def health_check(self) -> tuple[bool, str]:
try:
# 检查依赖服务
# ...
return True, "✅ Service is healthy"
except Exception as e:
return False, f"❌ Health check failed: {e}"
2. test_credentials()
测试凭证有效性:
def test_credentials(self, credentials: Dict[str, Any]) -> tuple[bool, str]:
api_key = credentials.get("api_key")
if self._validate_api_key(api_key):
return True, "✅ Credentials valid"
return False, "❌ Invalid API key"
3. on_init()
初始化回调:
def on_init(self, node_config: Dict[str, Any], workflow_entity: Optional[Dict[str, Any]]):
# 初始化资源、连接等
self.db_connection = self._connect_to_db()
上下文信息
execute() 方法的 context 参数包含:
{
"trace_id": "...", # W3C Trace ID
"span_id": "...", # W3C Span ID
"node_name": "...", # 节点名称
"node_type": "...", # 节点类型
"workflow_name": "...", # 工作流名称
"workflow_instance_id": "..." # 工作流实例 ID
}
输出类型
日志消息
yield {"type": "log", "message": "Processing data..."}
结果
yield {
"type": "result",
"data": {
"result": "output data",
"metadata": {...}
},
"branch_index": 0 # 可选
}
错误
yield {"type": "error", "message": "Error description"}
完整示例
查看以下示例:
- example_plugin.py - 简单的文本处理插件
- langchain_ollama_plugin.py - LangChain + Ollama 集成
项目结构
plugins/python-plugin-framework/
├── base_plugin.py # 框架核心
├── example_plugin.py # 示例插件
├── langchain_ollama_plugin.py # LangChain 插件
├── README.md # 本文档
└── requirements.txt # 依赖
依赖
基础框架依赖:
grpcio>=1.60.0
grpcio-reflection>=1.60.0
protobuf>=4.25.0
glog-python==1.0.0 # Go glog 兼容的日志库
将你的插件特定依赖添加到自己的 requirements.txt。
日志系统
框架使用 glog-python,与 Go glog 格式完全兼容:
# 简单日志
self.logger.info("Processing started")
# 格式化日志
self.logger.infof("Processing %d items", count)
# 带 trace_id 的日志
logger = self.logger.with_field(trace_id, "")
logger.info("Request completed")
# 错误日志
self.logger.with_error(e).error("Processing failed")
日志格式:
[2025-11-15 17:10:29.461] [info] [PluginName] file.py:10 [trace_id] [Node name] Message
最佳实践
1. 参数验证
def execute(self, parameters, parent_output, global_vars, context):
# 验证必需参数
if not parameters.get("required_param"):
yield {"type": "error", "message": "Missing required_param"}
return
# 验证参数类型
try:
value = int(parameters.get("number_param"))
except ValueError:
yield {"type": "error", "message": "number_param must be an integer"}
return
2. 错误处理
def execute(self, parameters, parent_output, global_vars, context):
try:
# 业务逻辑
result = self._process_data(parameters)
yield {"type": "result", "data": result}
except ValueError as e:
yield {"type": "error", "message": f"Invalid input: {e}"}
except Exception as e:
self.logger.error(f"Unexpected error: {e}")
yield {"type": "error", "message": f"Processing failed: {e}"}
3. 进度反馈
def execute(self, parameters, parent_output, global_vars, context):
items = parameters.get("items", [])
total = len(items)
for i, item in enumerate(items):
yield {"type": "log", "message": f"Processing {i+1}/{total}..."}
# 处理 item
yield {"type": "result", "data": {"processed": total}}
4. 流式输出
def execute(self, parameters, parent_output, global_vars, context):
# 适合长时间运行的任务
for chunk in self._stream_process(parameters):
yield {"type": "log", "message": f"Chunk: {chunk}"}
yield {"type": "result", "data": {"status": "completed"}}
调试
启用详细日志:
import logging
class MyPlugin(BasePluginService):
def __init__(self):
super().__init__(plugin_name="MyPlugin")
self.logger.setLevel(logging.DEBUG) # 启用 DEBUG 日志
常见问题
Q: 如何访问父节点的输出?
def execute(self, parameters, parent_output, global_vars, context):
# 父节点的输出在 parent_output 中
previous_result = parent_output.get("result")
yield {"type": "log", "message": f"Previous: {previous_result}"}
Q: 如何使用全局变量?
def execute(self, parameters, parent_output, global_vars, context):
# 全局变量在 global_vars 中
api_key = global_vars.get("api_key")
base_url = global_vars.get("base_url")
Q: 如何返回多个分支?
def execute(self, parameters, parent_output, global_vars, context):
# 分支 0
yield {
"type": "result",
"data": {"branch": "success"},
"branch_index": 0
}
# 分支 1
yield {
"type": "result",
"data": {"branch": "alternative"},
"branch_index": 1
}
贡献
欢迎提交 Issue 和 Pull Request!
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file python_workflow_plugin_framework-1.0.0.tar.gz.
File metadata
- Download URL: python_workflow_plugin_framework-1.0.0.tar.gz
- Upload date:
- Size: 30.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
01590816de972fd8fcfb0c81133c3466157766d2e565c282d51dfdaecc10d889
|
|
| MD5 |
489b330969b6924fc1ad2e1f1dc51629
|
|
| BLAKE2b-256 |
d620490add43c3ff3d2c10c3b6d2ae6723c870b405c647508c47e8a5bf5648d2
|
File details
Details for the file python_workflow_plugin_framework-1.0.0-py3-none-any.whl.
File metadata
- Download URL: python_workflow_plugin_framework-1.0.0-py3-none-any.whl
- Upload date:
- Size: 5.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
996e4ac489a7a63604dfccd38f7a4ebea61bf973b18b718976fc00aab21add54
|
|
| MD5 |
a0d374a157b9b807b86c48200ab28cd6
|
|
| BLAKE2b-256 |
2e9de8b45d20d93e56f2441b0b2eff8144a78a01347d024db292c38de79cedab
|