Feat/pipeline specified plugins (#1752)

* feat: add persistence field

* feat: add basic extension page in pipeline config

* Merge pull request #1751 from langbot-app/copilot/add-plugin-extension-tab

Implement pipeline-scoped plugin binding system

* fix: i18n keys

---------

Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
This commit is contained in:
Junyan Qin (Chin)
2025-11-06 12:51:33 +08:00
committed by GitHub
parent 2c2a89d9db
commit 4a84bf2355
30 changed files with 525 additions and 41 deletions
+14 -1
View File
@@ -68,6 +68,9 @@ class RuntimePipeline:
stage_containers: list[StageInstContainer]
"""阶段实例容器"""
bound_plugins: list[str]
"""绑定到此流水线的插件列表(格式:author/plugin_name"""
def __init__(
self,
@@ -78,9 +81,16 @@ class RuntimePipeline:
self.ap = ap
self.pipeline_entity = pipeline_entity
self.stage_containers = stage_containers
# Extract bound plugins from extensions_preferences
extensions_prefs = pipeline_entity.extensions_preferences or {}
plugin_list = extensions_prefs.get('plugins', [])
self.bound_plugins = [f"{p['author']}/{p['name']}" for p in plugin_list] if plugin_list else []
async def run(self, query: pipeline_query.Query):
query.pipeline_config = self.pipeline_entity.config
# Store bound plugins in query for filtering
query.variables['_pipeline_bound_plugins'] = self.bound_plugins
await self.process_query(query)
async def _check_output(self, query: pipeline_query.Query, result: pipeline_entities.StageProcessResult):
@@ -188,6 +198,9 @@ class RuntimePipeline:
async def process_query(self, query: pipeline_query.Query):
"""处理请求"""
try:
# Get bound plugins for this pipeline
bound_plugins = query.variables.get('_pipeline_bound_plugins', None)
# ======== 触发 MessageReceived 事件 ========
event_type = (
events.PersonMessageReceived
@@ -203,7 +216,7 @@ class RuntimePipeline:
message_chain=query.message_chain,
)
event_ctx = await self.ap.plugin_connector.emit_event(event_obj)
event_ctx = await self.ap.plugin_connector.emit_event(event_obj, bound_plugins)
if event_ctx.is_prevented_default():
return
+6 -2
View File
@@ -65,7 +65,9 @@ class PreProcessor(stage.PipelineStage):
query.use_llm_model_uuid = llm_model.model_entity.uuid
if llm_model.model_entity.abilities.__contains__('func_call'):
query.use_funcs = await self.ap.tool_mgr.get_all_tools()
# Get bound plugins for filtering tools
bound_plugins = query.variables.get('_pipeline_bound_plugins', None)
query.use_funcs = await self.ap.tool_mgr.get_all_tools(bound_plugins)
variables = {
'session_id': f'{query.session.launcher_type.value}_{query.session.launcher_id}',
@@ -130,7 +132,9 @@ class PreProcessor(stage.PipelineStage):
query=query,
)
event_ctx = await self.ap.plugin_connector.emit_event(event)
# Get bound plugins for filtering
bound_plugins = query.variables.get('_pipeline_bound_plugins', None)
event_ctx = await self.ap.plugin_connector.emit_event(event, bound_plugins)
query.prompt.messages = event_ctx.event.default_prompt
query.messages = event_ctx.event.prompt
+3 -1
View File
@@ -43,7 +43,9 @@ class ChatMessageHandler(handler.MessageHandler):
query=query,
)
event_ctx = await self.ap.plugin_connector.emit_event(event)
# Get bound plugins for filtering
bound_plugins = query.variables.get('_pipeline_bound_plugins', None)
event_ctx = await self.ap.plugin_connector.emit_event(event, bound_plugins)
is_create_card = False # 判断下是否需要创建流式卡片
+3 -1
View File
@@ -45,7 +45,9 @@ class CommandHandler(handler.MessageHandler):
query=query,
)
event_ctx = await self.ap.plugin_connector.emit_event(event)
# Get bound plugins for filtering
bound_plugins = query.variables.get('_pipeline_bound_plugins', None)
event_ctx = await self.ap.plugin_connector.emit_event(event, bound_plugins)
if event_ctx.is_prevented_default():
if event_ctx.event.reply_message_chain is not None:
+6 -2
View File
@@ -72,7 +72,9 @@ class ResponseWrapper(stage.PipelineStage):
query=query,
)
event_ctx = await self.ap.plugin_connector.emit_event(event)
# Get bound plugins for filtering
bound_plugins = query.variables.get('_pipeline_bound_plugins', None)
event_ctx = await self.ap.plugin_connector.emit_event(event, bound_plugins)
if event_ctx.is_prevented_default():
yield entities.StageProcessResult(
@@ -115,7 +117,9 @@ class ResponseWrapper(stage.PipelineStage):
query=query,
)
event_ctx = await self.ap.plugin_connector.emit_event(event)
# Get bound plugins for filtering
bound_plugins = query.variables.get('_pipeline_bound_plugins', None)
event_ctx = await self.ap.plugin_connector.emit_event(event, bound_plugins)
if event_ctx.is_prevented_default():
yield entities.StageProcessResult(