This commit is contained in:
Typer_Body
2026-05-18 01:47:13 +08:00
parent 27c0d344bf
commit bb7db53447
89 changed files with 1202 additions and 6883 deletions

View File

@@ -5,7 +5,7 @@ Node metadata is loaded from: ../../templates/metadata/nodes/call_pipeline.yaml
from __future__ import annotations
from typing import Any, ClassVar
from typing import Any
import langbot_plugin.api.definition.abstract.platform.adapter as abstract_platform_adapter
import langbot_plugin.api.entities.builtin.pipeline.query as pipeline_query
@@ -15,26 +15,13 @@ import langbot_plugin.api.entities.builtin.platform.message as platform_message
import langbot_plugin.api.entities.builtin.provider.session as provider_session
from ..entities import ExecutionContext
from ..node import WorkflowNode, workflow_node, NodePort, NodeConfig
from ..node import WorkflowNode, workflow_node
@workflow_node('call_pipeline')
class CallPipelineNode(WorkflowNode):
"""Call pipeline node - invoke an existing pipeline"""
type_name = 'call_pipeline'
category = 'action'
icon = 'Workflow'
name = 'call_pipeline'
description = 'call_pipeline'
name_zh = '调用 Pipeline'
name_en = 'Call Pipeline'
description_zh = '调用现有的 Pipeline 进行处理'
description_en = 'Invoke an existing Pipeline for processing'
inputs: ClassVar[list[NodePort]] = []
outputs: ClassVar[list[NodePort]] = []
config_schema: ClassVar[list[NodeConfig]] = []
async def execute(self, inputs: dict[str, Any], context: ExecutionContext) -> dict[str, Any]:
if not self.ap:
@@ -124,7 +111,6 @@ class CallPipelineNode(WorkflowNode):
if context.message_context and context.message_context.is_group:
group = platform_entities.Group(
id=context.message_context.group_id or context.session_id or 'workflow_group',
name='Workflow Group',
permission=platform_entities.Permission.Member,
)
sender = platform_entities.GroupMember(
@@ -152,7 +138,6 @@ class CallPipelineNode(WorkflowNode):
else None,
)
class _WorkflowPipelineCaptureAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
responses: list[dict[str, Any]] = []

View File

@@ -7,29 +7,16 @@ from __future__ import annotations
import json
import re
from typing import Any, ClassVar
from typing import Any
from ..entities import ExecutionContext
from ..node import WorkflowNode, workflow_node, NodePort, NodeConfig
from ..node import WorkflowNode, workflow_node
@workflow_node('code_executor')
class CodeExecutorNode(WorkflowNode):
"""Code executor node - run Python or JavaScript code"""
type_name = 'code_executor'
category = 'process'
icon = 'Code'
name = 'code_executor'
description = 'code_executor'
name_zh = '代码执行'
name_en = 'Code Executor'
description_zh = '执行自定义代码处理数据'
description_en = 'Execute custom code to process data'
inputs: ClassVar[list[NodePort]] = []
outputs: ClassVar[list[NodePort]] = []
config_schema: ClassVar[list[NodeConfig]] = []
async def execute(self, inputs: dict[str, Any], context: ExecutionContext) -> dict[str, Any]:
code = self.get_config('code', '')

View File

@@ -5,30 +5,17 @@ Node metadata is loaded from: ../../templates/metadata/nodes/condition.yaml
from __future__ import annotations
from typing import Any, ClassVar
from typing import Any
from ..entities import ExecutionContext
from ..node import WorkflowNode, workflow_node, NodePort, NodeConfig
from ..node import WorkflowNode, workflow_node
from ..safe_eval import safe_eval_with_vars
@workflow_node('condition')
class ConditionNode(WorkflowNode):
"""Condition node - branch based on condition"""
type_name = 'condition'
category = 'control'
icon = 'GitBranch'
name = 'condition'
description = 'condition'
name_zh = '条件分支'
name_en = 'Condition'
description_zh = '根据条件分支工作流'
description_en = 'Branch workflow based on a condition'
inputs: ClassVar[list[NodePort]] = []
outputs: ClassVar[list[NodePort]] = []
config_schema: ClassVar[list[NodeConfig]] = []
async def execute(self, inputs: dict[str, Any], context: ExecutionContext) -> dict[str, Any]:
condition_type = self.get_config('condition_type', 'expression')

View File

@@ -5,29 +5,16 @@ Node metadata is loaded from: ../../templates/metadata/nodes/coze_bot.yaml
from __future__ import annotations
from typing import Any, ClassVar
from typing import Any
from ..entities import ExecutionContext
from ..node import WorkflowNode, workflow_node, NodePort, NodeConfig
from ..node import WorkflowNode, workflow_node
@workflow_node('coze_bot')
class CozeBotNode(WorkflowNode):
"""Coze bot node - call Coze API bot"""
type_name = 'coze_bot'
category = 'integration'
icon = 'MessageSquare'
name = 'coze_bot'
description = 'coze_bot'
name_zh = 'Coze Bot'
name_en = 'Coze Bot'
description_zh = '调用扣子 Bot'
description_en = 'Call a Coze Bot'
inputs: ClassVar[list[NodePort]] = []
outputs: ClassVar[list[NodePort]] = []
config_schema: ClassVar[list[NodeConfig]] = []
async def execute(self, inputs: dict[str, Any], context: ExecutionContext) -> dict[str, Any]:
api_key = self.get_config('api_key', '')

View File

@@ -5,29 +5,16 @@ Node metadata is loaded from: ../../templates/metadata/nodes/cron_trigger.yaml
from __future__ import annotations
from typing import Any, ClassVar
from typing import Any
from ..entities import ExecutionContext
from ..node import WorkflowNode, workflow_node, NodePort, NodeConfig
from ..node import WorkflowNode, workflow_node
@workflow_node('cron_trigger')
class CronTriggerNode(WorkflowNode):
"""Cron trigger node - triggers workflow on schedule"""
type_name = 'cron_trigger'
category = 'trigger'
icon = 'Timer'
name = 'cron_trigger'
description = 'cron_trigger'
name_zh = '定时触发'
name_en = 'Scheduled Trigger'
description_zh = '按定时计划触发工作流'
description_en = 'Trigger workflow on a scheduled time'
inputs: ClassVar[list[NodePort]] = []
outputs: ClassVar[list[NodePort]] = []
config_schema: ClassVar[list[NodeConfig]] = []
async def execute(self, inputs: dict[str, Any], context: ExecutionContext) -> dict[str, Any]:
from datetime import datetime

View File

@@ -5,30 +5,17 @@ Node metadata is loaded from: ../../templates/metadata/nodes/data_transform.yaml
from __future__ import annotations
from typing import Any, ClassVar
from typing import Any
from ..entities import ExecutionContext
from ..node import WorkflowNode, workflow_node, NodePort, NodeConfig
from ..node import WorkflowNode, workflow_node
from ..safe_eval import safe_eval_with_vars
@workflow_node('data_transform')
class DataTransformNode(WorkflowNode):
"""Data transform node - transform data using templates or JSONPath"""
type_name = 'data_transform'
category = 'process'
icon = 'ArrowRightLeft'
name = 'data_transform'
description = 'data_transform'
name_zh = '数据转换'
name_en = 'Data Transform'
description_zh = '使用模板或 JSONPath 转换数据'
description_en = 'Transform data using templates or JSONPath'
inputs: ClassVar[list[NodePort]] = []
outputs: ClassVar[list[NodePort]] = []
config_schema: ClassVar[list[NodeConfig]] = []
async def execute(self, inputs: dict[str, Any], context: ExecutionContext) -> dict[str, Any]:
data = inputs.get('data')

View File

@@ -5,29 +5,16 @@ Node metadata is loaded from: ../../templates/metadata/nodes/database_query.yaml
from __future__ import annotations
from typing import Any, ClassVar
from typing import Any
from ..entities import ExecutionContext
from ..node import WorkflowNode, workflow_node, NodePort, NodeConfig
from ..node import WorkflowNode, workflow_node
@workflow_node('database_query')
class DatabaseQueryNode(WorkflowNode):
"""Database query node - execute database queries"""
type_name = 'database_query'
category = 'integration'
icon = 'Database'
name = 'database_query'
description = 'database_query'
name_zh = '数据库查询'
name_en = 'Database Query'
description_zh = '执行数据库查询'
description_en = 'Execute database queries'
inputs: ClassVar[list[NodePort]] = []
outputs: ClassVar[list[NodePort]] = []
config_schema: ClassVar[list[NodeConfig]] = []
async def execute(self, inputs: dict[str, Any], context: ExecutionContext) -> dict[str, Any]:
connection_type = self.get_config('connection_type', 'postgresql')

View File

@@ -5,29 +5,16 @@ Node metadata is loaded from: ../../templates/metadata/nodes/dify_knowledge_quer
from __future__ import annotations
from typing import Any, ClassVar
from typing import Any
from ..entities import ExecutionContext
from ..node import WorkflowNode, workflow_node, NodePort, NodeConfig
from ..node import WorkflowNode, workflow_node
@workflow_node('dify_knowledge_query')
class DifyKnowledgeQueryNode(WorkflowNode):
"""Dify knowledge base query node - query Dify knowledge base"""
type_name = 'dify_knowledge_query'
category = 'integration'
icon = 'BookOpen'
name = 'dify_knowledge_query'
description = 'dify_knowledge_query'
name_zh = 'Dify 知识库查询'
name_en = 'Dify Knowledge Query'
description_zh = '查询 Dify 知识库'
description_en = 'Query Dify knowledge base'
inputs: ClassVar[list[NodePort]] = []
outputs: ClassVar[list[NodePort]] = []
config_schema: ClassVar[list[NodeConfig]] = []
async def execute(self, inputs: dict[str, Any], context: ExecutionContext) -> dict[str, Any]:
base_url = self.get_config('base_url', 'https://api.dify.ai/v1')

View File

@@ -5,29 +5,16 @@ Node metadata is loaded from: ../../templates/metadata/nodes/dify_workflow.yaml
from __future__ import annotations
from typing import Any, ClassVar
from typing import Any
from ..entities import ExecutionContext
from ..node import WorkflowNode, workflow_node, NodePort, NodeConfig
from ..node import WorkflowNode, workflow_node
@workflow_node('dify_workflow')
class DifyWorkflowNode(WorkflowNode):
"""Dify workflow node - call Dify service API"""
type_name = 'dify_workflow'
category = 'integration'
icon = 'Bot'
name = 'dify_workflow'
description = 'dify_workflow'
name_zh = 'Dify 工作流'
name_en = 'Dify Workflow'
description_zh = '调用 Dify 平台工作流'
description_en = 'Call a Dify platform workflow'
inputs: ClassVar[list[NodePort]] = []
outputs: ClassVar[list[NodePort]] = []
config_schema: ClassVar[list[NodeConfig]] = []
async def execute(self, inputs: dict[str, Any], context: ExecutionContext) -> dict[str, Any]:
base_url = self.get_config('base_url', 'https://api.dify.ai/v1')

View File

@@ -5,29 +5,16 @@ Node metadata is loaded from: ../../templates/metadata/nodes/end.yaml
from __future__ import annotations
from typing import Any, ClassVar
from typing import Any
from ..entities import ExecutionContext
from ..node import WorkflowNode, workflow_node, NodePort, NodeConfig
from ..node import WorkflowNode, workflow_node
@workflow_node('end')
class EndNode(WorkflowNode):
"""End node - marks the end of workflow execution"""
type_name = 'end'
category = 'action'
icon = 'PauseCircle'
name = 'end'
description = 'end'
name_zh = '结束'
name_en = 'End'
description_zh = '结束工作流执行'
description_en = 'End the workflow execution'
inputs: ClassVar[list[NodePort]] = []
outputs: ClassVar[list[NodePort]] = []
config_schema: ClassVar[list[NodeConfig]] = []
category = 'control'
async def execute(self, inputs: dict[str, Any], context: ExecutionContext) -> dict[str, Any]:
result = inputs.get('result')

View File

@@ -5,29 +5,16 @@ Node metadata is loaded from: ../../templates/metadata/nodes/event_trigger.yaml
from __future__ import annotations
from typing import Any, ClassVar
from typing import Any
from ..entities import ExecutionContext
from ..node import WorkflowNode, workflow_node, NodePort, NodeConfig
from ..node import WorkflowNode, workflow_node
@workflow_node('event_trigger')
class EventTriggerNode(WorkflowNode):
"""Event trigger node - triggers workflow on system events"""
type_name = 'event_trigger'
category = 'trigger'
icon = 'Bell'
name = 'event_trigger'
description = 'event_trigger'
name_zh = '事件触发'
name_en = 'Event Trigger'
description_zh = '当系统事件发生时触发工作流'
description_en = 'Trigger workflow when a system event occurs'
inputs: ClassVar[list[NodePort]] = []
outputs: ClassVar[list[NodePort]] = []
config_schema: ClassVar[list[NodeConfig]] = []
async def execute(self, inputs: dict[str, Any], context: ExecutionContext) -> dict[str, Any]:
from datetime import datetime

View File

@@ -5,29 +5,16 @@ Node metadata is loaded from: ../../templates/metadata/nodes/http_request.yaml
from __future__ import annotations
from typing import Any, ClassVar
from typing import Any
from ..entities import ExecutionContext
from ..node import WorkflowNode, workflow_node, NodePort, NodeConfig
from ..node import WorkflowNode, workflow_node
@workflow_node('http_request')
class HTTPRequestNode(WorkflowNode):
"""HTTP request node - make HTTP API calls"""
type_name = 'http_request'
category = 'process'
icon = 'Globe'
name = 'http_request'
description = 'http_request'
name_zh = 'HTTP 请求'
name_en = 'HTTP Request'
description_zh = '向外部 API 发送 HTTP 请求'
description_en = 'Make HTTP requests to external APIs'
inputs: ClassVar[list[NodePort]] = []
outputs: ClassVar[list[NodePort]] = []
config_schema: ClassVar[list[NodeConfig]] = []
category = 'action'
async def execute(self, inputs: dict[str, Any], context: ExecutionContext) -> dict[str, Any]:
import aiohttp

View File

@@ -2,47 +2,16 @@
from __future__ import annotations
from typing import Any, ClassVar
from typing import Any
from ..entities import ExecutionContext
from ..node import WorkflowNode, workflow_node, NodePort, NodeConfig
from ..node import WorkflowNode, workflow_node
@workflow_node('iterator')
class IteratorNode(WorkflowNode):
"""Iterator node - iterate over array items one by one"""
type_name = 'iterator'
category = 'control'
icon = 'Repeat'
name = 'iterator'
name_zh = '迭代器'
name_en = 'Iterator'
description = 'iterator'
description_zh = '逐个遍历数组元素'
description_en = 'Iterate over array elements one by one'
inputs: ClassVar[list[NodePort]] = [
NodePort(name='items', type='array', description='Array to iterate over', required=True),
]
outputs: ClassVar[list[NodePort]] = [
NodePort(name='item', type='any', description='Current item'),
NodePort(name='index', type='number', description='Current index'),
NodePort(name='is_first', type='boolean', description='Whether this is the first item'),
NodePort(name='is_last', type='boolean', description='Whether this is the last item'),
NodePort(name='results', type='array', description='All iteration results'),
NodePort(name='completed', type='boolean', description='Whether iteration completed'),
]
config_schema: ClassVar[list[NodeConfig]] = [
NodeConfig(
name='max_iterations',
type='integer',
required=False,
default=1000,
description='Maximum iterations (safety limit)',
label={'en_US': 'Max Iterations', 'zh_Hans': '最大迭代次数'},
),
]
async def execute(self, inputs: dict[str, Any], context: ExecutionContext) -> dict[str, Any]:
items = inputs.get('items', [])

View File

@@ -5,29 +5,16 @@ Node metadata is loaded from: ../../templates/metadata/nodes/knowledge_retrieval
from __future__ import annotations
from typing import Any, ClassVar
from typing import Any
from ..entities import ExecutionContext
from ..node import WorkflowNode, workflow_node, NodePort, NodeConfig
from ..node import WorkflowNode, workflow_node
@workflow_node('knowledge_retrieval')
class KnowledgeRetrievalNode(WorkflowNode):
"""Knowledge retrieval node - search in knowledge base"""
type_name = 'knowledge_retrieval'
category = 'process'
icon = 'Search'
name = 'knowledge_retrieval'
description = 'knowledge_retrieval'
name_zh = '知识库检索'
name_en = 'Knowledge Retrieval'
description_zh = '从知识库中检索相关信息'
description_en = 'Retrieve relevant information from knowledge bases'
inputs: ClassVar[list[NodePort]] = []
outputs: ClassVar[list[NodePort]] = []
config_schema: ClassVar[list[NodeConfig]] = []
async def execute(self, inputs: dict[str, Any], context: ExecutionContext) -> dict[str, Any]:
query = inputs.get('query', '')

View File

@@ -5,29 +5,16 @@ Node metadata is loaded from: ../../templates/metadata/nodes/langflow_flow.yaml
from __future__ import annotations
from typing import Any, ClassVar
from typing import Any
from ..entities import ExecutionContext
from ..node import WorkflowNode, workflow_node, NodePort, NodeConfig
from ..node import WorkflowNode, workflow_node
@workflow_node('langflow_flow')
class LangflowFlowNode(WorkflowNode):
"""Langflow flow node - call Langflow API"""
type_name = 'langflow_flow'
category = 'integration'
icon = 'GitBranch'
name = 'langflow_flow'
description = 'langflow_flow'
name_zh = 'Langflow 流程'
name_en = 'Langflow Flow'
description_zh = '调用 Langflow 流程'
description_en = 'Call a Langflow flow'
inputs: ClassVar[list[NodePort]] = []
outputs: ClassVar[list[NodePort]] = []
config_schema: ClassVar[list[NodeConfig]] = []
async def execute(self, inputs: dict[str, Any], context: ExecutionContext) -> dict[str, Any]:
base_url = self.get_config('base_url', 'http://localhost:7860')

View File

@@ -4,81 +4,20 @@ from __future__ import annotations
import logging
import re
from typing import Any, ClassVar
from typing import Any
import langbot_plugin.api.entities.builtin.provider.message as provider_message
from ..entities import ExecutionContext
from ..node import WorkflowNode, workflow_node, NodePort, NodeConfig
from ..node import WorkflowNode, workflow_node
logger = logging.getLogger(__name__)
@workflow_node('llm_call')
class LLMCallNode(WorkflowNode):
"""LLM call node - invoke large language model"""
type_name = 'llm_call'
category = 'process'
icon = 'Brain'
name = 'llm_call'
name_zh = 'LLM 调用'
name_en = 'LLM Call'
description = 'llm_call'
description_zh = '调用大语言模型生成响应'
description_en = 'Call a large language model to generate responses'
inputs: ClassVar[list[NodePort]] = [
NodePort(name='input', type='string', description='Input text to send to the model', required=False),
NodePort(name='context', type='object', description='Additional context data', required=False),
]
outputs: ClassVar[list[NodePort]] = [
NodePort(name='response', type='string', description='Model response text'),
NodePort(name='usage', type='object', description='Token usage information'),
]
config_schema: ClassVar[list[NodeConfig]] = [
NodeConfig(
name='model',
type='llm-model-selector',
required=True,
description='Select the LLM model to use',
label={'en_US': 'Model', 'zh_Hans': '模型'},
),
NodeConfig(
name='system_prompt',
type='textarea',
required=False,
default='',
description='System prompt to set model behavior',
label={'en_US': 'System Prompt', 'zh_Hans': '系统提示词'},
),
NodeConfig(
name='user_prompt_template',
type='textarea',
required=True,
default='{{input}}',
description='User prompt template with variable placeholders',
label={'en_US': 'User Prompt Template', 'zh_Hans': '用户提示词模板'},
),
NodeConfig(
name='temperature',
type='number',
required=False,
default=0.7,
description='Controls randomness (0.0-2.0)',
label={'en_US': 'Temperature', 'zh_Hans': '温度'},
min_value=0.0,
max_value=2.0,
),
NodeConfig(
name='max_tokens',
type='integer',
required=False,
default=0,
description='Max tokens to generate (0 = model default)',
label={'en_US': 'Max Tokens', 'zh_Hans': '最大令牌数'},
),
]
def _resolve_template(self, template: str, inputs: dict[str, Any], context: ExecutionContext) -> str:
"""Resolve {{variable}} placeholders in a template string."""

View File

@@ -2,54 +2,16 @@
from __future__ import annotations
from typing import Any, ClassVar
from typing import Any
from ..entities import ExecutionContext
from ..node import WorkflowNode, workflow_node, NodePort, NodeConfig
from ..node import WorkflowNode, workflow_node
@workflow_node('loop')
class LoopNode(WorkflowNode):
"""Loop node - iterate over items"""
type_name = 'loop'
category = 'control'
icon = 'Repeat'
name = 'loop'
name_zh = '循环'
name_en = 'Loop'
description = 'loop'
description_zh = '遍历项目或重复直到满足条件'
description_en = 'Iterate over items or repeat until condition'
inputs: ClassVar[list[NodePort]] = [
NodePort(name='items', type='array', description='Items to iterate over', required=False),
]
outputs: ClassVar[list[NodePort]] = [
NodePort(name='item', type='any', description='Current item in iteration'),
NodePort(name='index', type='number', description='Current iteration index'),
NodePort(name='results', type='array', description='All iteration results'),
NodePort(name='completed', type='boolean', description='Whether loop completed'),
]
config_schema: ClassVar[list[NodeConfig]] = [
NodeConfig(
name='loop_type',
type='select',
required=True,
default='foreach',
description='Type of loop',
label={'en_US': 'Loop Type', 'zh_Hans': '循环类型'},
options=['foreach', 'while', 'count'],
),
NodeConfig(
name='max_iterations',
type='integer',
required=False,
default=100,
description='Maximum iterations (safety limit)',
label={'en_US': 'Max Iterations', 'zh_Hans': '最大迭代次数'},
),
]
async def execute(self, inputs: dict[str, Any], context: ExecutionContext) -> dict[str, Any]:
items = inputs.get('items', [])

View File

@@ -9,36 +9,24 @@ The i18n for label and description is handled on the frontend side.
from __future__ import annotations
from typing import Any, ClassVar
from typing import Any
from ..entities import ExecutionContext
from ..node import WorkflowNode, workflow_node, NodePort, NodeConfig
from ..node import WorkflowNode, workflow_node
@workflow_node('mcp_tool')
class MCPToolNode(WorkflowNode):
"""MCP tool node - invoke MCP (Model Context Protocol) tools"""
# Node type for registration
type_name = 'mcp_tool'
# Category and icon - these are not i18n
category = 'integration'
icon = 'Wrench'
# Name and description - i18n handled on frontend side
# Frontend will use node type key to look up translation
name = 'mcp_tool'
description = 'mcp_tool'
name_zh = 'MCP 工具'
name_en = 'MCP Tool'
description_zh = '调用 MCP 工具'
description_en = 'Invoke an MCP (Model Context Protocol) tool'
# Inputs/outputs/config - loaded from YAML at runtime
inputs: ClassVar[list[NodePort]] = []
outputs: ClassVar[list[NodePort]] = []
config_schema: ClassVar[list[NodeConfig]] = []
async def execute(self, inputs: dict[str, Any], context: ExecutionContext) -> dict[str, Any]:
"""Execute the MCP tool node

View File

@@ -5,11 +5,10 @@ Node metadata is loaded from: ../../templates/metadata/nodes/memory_store.yaml
from __future__ import annotations
from typing import Any, ClassVar
from typing import Any
from ..entities import ExecutionContext
from ..node import WorkflowNode, workflow_node, NodePort, NodeConfig
from ..node import WorkflowNode, workflow_node
class MemoryHelper:
"""Helper class wrapping context.memory dict with get/set/delete/list_all/append operations"""
@@ -47,24 +46,11 @@ class MemoryHelper:
self.set(key, current, scope=scope, ttl=ttl)
return current
@workflow_node('memory_store')
class MemoryStoreNode(WorkflowNode):
"""Memory store node - store and retrieve from workflow memory"""
type_name = 'memory_store'
category = 'integration'
icon = 'HardDrive'
name = 'memory_store'
description = 'memory_store'
name_zh = '记忆存储'
name_en = 'Memory Store'
description_zh = '从工作流记忆中存储和检索数据'
description_en = 'Store and retrieve data from workflow memory'
inputs: ClassVar[list[NodePort]] = []
outputs: ClassVar[list[NodePort]] = []
config_schema: ClassVar[list[NodeConfig]] = []
async def execute(self, inputs: dict[str, Any], context: ExecutionContext) -> dict[str, Any]:
operation = self.get_config('operation', 'get')

View File

@@ -5,29 +5,16 @@ Node metadata is loaded from: ../../templates/metadata/nodes/merge.yaml
from __future__ import annotations
from typing import Any, ClassVar
from typing import Any
from ..entities import ExecutionContext
from ..node import WorkflowNode, workflow_node, NodePort, NodeConfig
from ..node import WorkflowNode, workflow_node
@workflow_node('merge')
class MergeNode(WorkflowNode):
"""Merge node - combine multiple inputs"""
type_name = 'merge'
category = 'control'
icon = 'GitMerge'
name = 'merge'
description = 'merge'
name_zh = '合并'
name_en = 'Merge'
description_zh = '将多个分支合并在一起'
description_en = 'Merge multiple branches back together'
inputs: ClassVar[list[NodePort]] = []
outputs: ClassVar[list[NodePort]] = []
config_schema: ClassVar[list[NodeConfig]] = []
async def execute(self, inputs: dict[str, Any], context: ExecutionContext) -> dict[str, Any]:
strategy = self.get_config('merge_strategy', 'object')

View File

@@ -7,29 +7,16 @@ Node metadata (label, description, inputs, outputs, config) is loaded from:
from __future__ import annotations
from typing import Any, ClassVar
from typing import Any
from ..entities import ExecutionContext
from ..node import WorkflowNode, workflow_node, NodePort, NodeConfig
from ..node import WorkflowNode, workflow_node
@workflow_node('message_trigger')
class MessageTriggerNode(WorkflowNode):
"""Message trigger node - triggers workflow on message arrival"""
type_name = 'message_trigger'
category = 'trigger'
icon = 'MessageSquare'
name = 'message_trigger'
description = 'message_trigger'
name_zh = '消息触发'
name_en = 'Message Trigger'
description_zh = '当收到消息时触发工作流'
description_en = 'Trigger workflow when a message is received'
inputs: ClassVar[list[NodePort]] = []
outputs: ClassVar[list[NodePort]] = []
config_schema: ClassVar[list[NodeConfig]] = []
async def execute(self, inputs: dict[str, Any], context: ExecutionContext) -> dict[str, Any]:
msg_ctx = context.message_context

View File

@@ -5,29 +5,16 @@ Node metadata is loaded from: ../../templates/metadata/nodes/n8n_workflow.yaml
from __future__ import annotations
from typing import Any, ClassVar
from typing import Any
from ..entities import ExecutionContext
from ..node import WorkflowNode, workflow_node, NodePort, NodeConfig
from ..node import WorkflowNode, workflow_node
@workflow_node('n8n_workflow')
class N8nWorkflowNode(WorkflowNode):
"""n8n workflow node - call n8n workflow API"""
type_name = 'n8n_workflow'
category = 'integration'
icon = 'Workflow'
name = 'n8n_workflow'
description = 'n8n_workflow'
name_zh = 'n8n 工作流'
name_en = 'N8n Workflow'
description_zh = '通过 webhook 调用 n8n 工作流'
description_en = 'Call an n8n workflow via webhook'
inputs: ClassVar[list[NodePort]] = []
outputs: ClassVar[list[NodePort]] = []
config_schema: ClassVar[list[NodeConfig]] = []
async def execute(self, inputs: dict[str, Any], context: ExecutionContext) -> dict[str, Any]:
webhook_url = self.get_config('webhook_url', '')

View File

@@ -5,29 +5,16 @@ Node metadata is loaded from: ../../templates/metadata/nodes/opening_statement.y
from __future__ import annotations
from typing import Any, ClassVar
from typing import Any
from ..entities import ExecutionContext
from ..node import WorkflowNode, workflow_node, NodePort, NodeConfig
from ..node import WorkflowNode, workflow_node
@workflow_node('opening_statement')
class OpeningStatementNode(WorkflowNode):
"""Opening statement node - provide conversation opener and suggested questions"""
type_name = 'opening_statement'
category = 'action'
icon = 'MessageSquare'
name = 'opening_statement'
description = 'opening_statement'
name_zh = '对话开场白'
name_en = 'Opening Statement'
description_zh = '提供对话开场白和建议问题'
description_en = 'Provide conversation opener and suggested questions'
inputs: ClassVar[list[NodePort]] = []
outputs: ClassVar[list[NodePort]] = []
config_schema: ClassVar[list[NodeConfig]] = []
async def execute(self, inputs: dict[str, Any], context: ExecutionContext) -> dict[str, Any]:
statement = self.get_config('statement', '')

View File

@@ -2,51 +2,16 @@
from __future__ import annotations
from typing import Any, ClassVar
from typing import Any
from ..entities import ExecutionContext
from ..node import WorkflowNode, workflow_node, NodePort, NodeConfig
from ..node import WorkflowNode, workflow_node
@workflow_node('parallel')
class ParallelNode(WorkflowNode):
"""Parallel node - execute multiple branches simultaneously"""
type_name = 'parallel'
category = 'control'
icon = 'Layers'
name = 'parallel'
name_zh = '并行执行'
name_en = 'Parallel'
description = 'parallel'
description_zh = '并行执行多个分支'
description_en = 'Execute multiple branches in parallel'
inputs: ClassVar[list[NodePort]] = [
NodePort(name='input', type='any', description='Input data for all branches', required=False),
]
outputs: ClassVar[list[NodePort]] = [
NodePort(name='results', type='object', description='Combined results from all branches'),
NodePort(name='errors', type='array', description='Errors from branches (if any)'),
]
config_schema: ClassVar[list[NodeConfig]] = [
NodeConfig(
name='wait_all',
type='boolean',
required=False,
default=True,
description='Wait for all branches to complete',
label={'en_US': 'Wait for All', 'zh_Hans': '等待全部完成'},
),
NodeConfig(
name='fail_fast',
type='boolean',
required=False,
default=False,
description='Stop all branches if any fails',
label={'en_US': 'Fail Fast', 'zh_Hans': '快速失败'},
),
]
async def execute(self, inputs: dict[str, Any], context: ExecutionContext) -> dict[str, Any]:
return {

View File

@@ -5,29 +5,17 @@ Node metadata is loaded from: ../../templates/metadata/nodes/parameter_extractor
from __future__ import annotations
from typing import Any, ClassVar
from typing import Any
from ..entities import ExecutionContext
from ..node import WorkflowNode, workflow_node, NodePort, NodeConfig
from ..node import WorkflowNode, workflow_node
@workflow_node('parameter_extractor')
class ParameterExtractorNode(WorkflowNode):
"""Parameter extractor node - extract structured parameters from text"""
type_name = 'parameter_extractor'
category = 'process'
icon: str = 'Variable'
name = 'parameter_extractor'
description = 'parameter_extractor'
name_zh = '参数提取器'
name_en = 'Parameter Extractor'
description_zh = '使用 AI 从文本中提取结构化参数'
description_en = 'Extract structured parameters from text using AI'
inputs: ClassVar[list[NodePort]] = []
outputs: ClassVar[list[NodePort]] = []
config_schema: ClassVar[list[NodeConfig]] = []
async def execute(self, inputs: dict[str, Any], context: ExecutionContext) -> dict[str, Any]:
param_defs = self.get_config('parameters', [])

View File

@@ -5,11 +5,10 @@
# from __future__ import annotations
# from typing import Any, ClassVar
# from typing import Any
# from ..entities import ExecutionContext
# from ..node import WorkflowNode, workflow_node, NodePort, NodeConfig
# from ..node import WorkflowNode, workflow_node
# @workflow_node('plugin_call')
# class PluginCallNode(WorkflowNode):

View File

@@ -5,29 +5,16 @@ Node metadata is loaded from: ../../templates/metadata/nodes/question_classifier
from __future__ import annotations
from typing import Any, ClassVar
from typing import Any
from ..entities import ExecutionContext
from ..node import WorkflowNode, workflow_node, NodePort, NodeConfig
from ..node import WorkflowNode, workflow_node
@workflow_node('question_classifier')
class QuestionClassifierNode(WorkflowNode):
"""Question classifier node - classify user questions into categories"""
type_name = 'question_classifier'
category = 'process'
icon = 'ListFilter'
name = 'question_classifier'
description = 'question_classifier'
name_zh = '问题分类器'
name_en = 'Question Classifier'
description_zh = '使用 AI 将问题分类到预定义类别'
description_en = 'Classify questions into predefined categories using AI'
inputs: ClassVar[list[NodePort]] = []
outputs: ClassVar[list[NodePort]] = []
config_schema: ClassVar[list[NodeConfig]] = []
async def execute(self, inputs: dict[str, Any], context: ExecutionContext) -> dict[str, Any]:
categories = self.get_config('categories', [])

View File

@@ -5,29 +5,16 @@ Node metadata is loaded from: ../../templates/metadata/nodes/redis_operation.yam
from __future__ import annotations
from typing import Any, ClassVar
from typing import Any
from ..entities import ExecutionContext
from ..node import WorkflowNode, workflow_node, NodePort, NodeConfig
from ..node import WorkflowNode, workflow_node
@workflow_node('redis_operation')
class RedisOperationNode(WorkflowNode):
"""Redis operation node - perform Redis cache operations"""
type_name = 'redis_operation'
category = 'integration'
icon = 'Server'
name = 'redis_operation'
description = 'redis_operation'
name_zh = 'Redis 操作'
name_en = 'Redis Operation'
description_zh = '执行 Redis 缓存操作'
description_en = 'Perform Redis cache operations'
inputs: ClassVar[list[NodePort]] = []
outputs: ClassVar[list[NodePort]] = []
config_schema: ClassVar[list[NodeConfig]] = []
async def execute(self, inputs: dict[str, Any], context: ExecutionContext) -> dict[str, Any]:
connection_url = self.get_config('connection_url', 'redis://localhost:6379')

View File

@@ -6,31 +6,18 @@ Node metadata is loaded from: ../../templates/metadata/nodes/reply_message.yaml
from __future__ import annotations
import logging
from typing import Any, ClassVar
from typing import Any
from ..entities import ExecutionContext
from ..node import WorkflowNode, workflow_node, NodePort, NodeConfig
from ..node import WorkflowNode, workflow_node
logger = logging.getLogger(__name__)
@workflow_node('reply_message')
class ReplyMessageNode(WorkflowNode):
"""Reply message node - reply to the triggering message"""
type_name = 'reply_message'
category = 'action'
icon = 'Send'
name = 'reply_message'
description = 'reply_message'
name_zh = '回复消息'
name_en = 'Reply Message'
description_zh = '回复触发工作流的消息'
description_en = 'Reply to the message that triggered the workflow'
inputs: ClassVar[list[NodePort]] = []
outputs: ClassVar[list[NodePort]] = []
config_schema: ClassVar[list[NodeConfig]] = []
async def execute(self, inputs: dict[str, Any], context: ExecutionContext) -> dict[str, Any]:
message = inputs.get('message')

View File

@@ -5,29 +5,16 @@ Node metadata is loaded from: ../../templates/metadata/nodes/send_message.yaml
from __future__ import annotations
from typing import Any, ClassVar
from typing import Any
from ..entities import ExecutionContext
from ..node import WorkflowNode, workflow_node, NodePort, NodeConfig
from ..node import WorkflowNode, workflow_node
@workflow_node('send_message')
class SendMessageNode(WorkflowNode):
"""Send message node - send message to a target"""
type_name = 'send_message'
category = 'action'
icon = 'MessageCircle'
name = 'send_message'
description = 'send_message'
name_zh = '发送消息'
name_en = 'Send Message'
description_zh = '向聊天或用户发送消息'
description_en = 'Send a message to a chat or user'
inputs: ClassVar[list[NodePort]] = []
outputs: ClassVar[list[NodePort]] = []
config_schema: ClassVar[list[NodeConfig]] = []
async def execute(self, inputs: dict[str, Any], context: ExecutionContext) -> dict[str, Any]:
return {'status': 'sent', 'message_id': f'msg_{context.execution_id}'}

View File

@@ -5,29 +5,16 @@ Node metadata is loaded from: ../../templates/metadata/nodes/set_variable.yaml
from __future__ import annotations
from typing import Any, ClassVar
from typing import Any
from ..entities import ExecutionContext
from ..node import WorkflowNode, workflow_node, NodePort, NodeConfig
from ..node import WorkflowNode, workflow_node
@workflow_node('set_variable')
class SetVariableNode(WorkflowNode):
"""Set variable node - set workflow or conversation variable"""
type_name = 'set_variable'
category = 'action'
icon = 'Variable'
name = 'set_variable'
description = 'set_variable'
name_zh = '设置变量'
name_en = 'Set Variable'
description_zh = '设置上下文变量值'
description_en = 'Set a context variable value'
inputs: ClassVar[list[NodePort]] = []
outputs: ClassVar[list[NodePort]] = []
config_schema: ClassVar[list[NodeConfig]] = []
async def execute(self, inputs: dict[str, Any], context: ExecutionContext) -> dict[str, Any]:
value = inputs.get('value')

View File

@@ -5,29 +5,16 @@ Node metadata is loaded from: ../../templates/metadata/nodes/store_data.yaml
from __future__ import annotations
from typing import Any, ClassVar
from typing import Any
from ..entities import ExecutionContext
from ..node import WorkflowNode, workflow_node, NodePort, NodeConfig
from ..node import WorkflowNode, workflow_node
@workflow_node('store_data')
class StoreDataNode(WorkflowNode):
"""Store data node - save data to storage"""
type_name = 'store_data'
category = 'action'
icon = 'Database'
name = 'store_data'
description = 'store_data'
name_zh = '存储数据'
name_en = 'Store Data'
description_zh = '将数据存储到持久化存储'
description_en = 'Store data to persistent storage'
inputs: ClassVar[list[NodePort]] = []
outputs: ClassVar[list[NodePort]] = []
config_schema: ClassVar[list[NodeConfig]] = []
async def execute(self, inputs: dict[str, Any], context: ExecutionContext) -> dict[str, Any]:
key = inputs.get('key', '')

View File

@@ -5,29 +5,16 @@ Node metadata is loaded from: ../../templates/metadata/nodes/switch.yaml
from __future__ import annotations
from typing import Any, ClassVar
from typing import Any
from ..entities import ExecutionContext
from ..node import WorkflowNode, workflow_node, NodePort, NodeConfig
from ..node import WorkflowNode, workflow_node
@workflow_node('switch')
class SwitchNode(WorkflowNode):
"""Switch node - multi-way branch based on value"""
type_name = 'switch'
category = 'control'
icon = 'Split'
name = 'switch'
description = 'switch'
name_zh = '多路分支'
name_en = 'Switch'
description_zh = '根据多个条件分支工作流'
description_en = 'Branch workflow based on multiple cases'
inputs: ClassVar[list[NodePort]] = []
outputs: ClassVar[list[NodePort]] = []
config_schema: ClassVar[list[NodeConfig]] = []
async def execute(self, inputs: dict[str, Any], context: ExecutionContext) -> dict[str, Any]:
expression = self.get_config('expression', '')

View File

@@ -5,29 +5,16 @@ Node metadata is loaded from: ../../templates/metadata/nodes/variable_aggregator
from __future__ import annotations
from typing import Any, ClassVar
from typing import Any
from ..entities import ExecutionContext
from ..node import WorkflowNode, workflow_node, NodePort, NodeConfig
from ..node import WorkflowNode, workflow_node
@workflow_node('variable_aggregator')
class VariableAggregatorNode(WorkflowNode):
"""Variable aggregator node - aggregate variables from multiple branches"""
type_name = 'variable_aggregator'
category = 'control'
icon = 'GitMerge'
name = 'variable_aggregator'
description = 'variable_aggregator'
name_zh = '变量聚合器'
name_en = 'Variable Aggregator'
description_zh = '聚合多个分支的变量输出'
description_en = 'Aggregate variable outputs from multiple branches'
inputs: ClassVar[list[NodePort]] = []
outputs: ClassVar[list[NodePort]] = []
config_schema: ClassVar[list[NodeConfig]] = []
async def execute(self, inputs: dict[str, Any], context: ExecutionContext) -> dict[str, Any]:
variables = inputs.get('variables', {})

View File

@@ -5,29 +5,16 @@ Node metadata is loaded from: ../../templates/metadata/nodes/wait.yaml
from __future__ import annotations
from typing import Any, ClassVar
from typing import Any
from ..entities import ExecutionContext
from ..node import WorkflowNode, workflow_node, NodePort, NodeConfig
from ..node import WorkflowNode, workflow_node
@workflow_node('wait')
class WaitNode(WorkflowNode):
"""Wait node - pause execution for a duration"""
type_name = 'wait'
category = 'control'
icon = 'Clock'
name = 'wait'
description = 'wait'
name_zh = '等待'
name_en = 'Wait'
description_zh = '暂停工作流执行指定时间'
description_en = 'Pause workflow execution for a specified duration'
inputs: ClassVar[list[NodePort]] = []
outputs: ClassVar[list[NodePort]] = []
config_schema: ClassVar[list[NodeConfig]] = []
async def execute(self, inputs: dict[str, Any], context: ExecutionContext) -> dict[str, Any]:
import asyncio

View File

@@ -5,29 +5,16 @@ Node metadata is loaded from: ../../templates/metadata/nodes/webhook_trigger.yam
from __future__ import annotations
from typing import Any, ClassVar
from typing import Any
from ..entities import ExecutionContext
from ..node import WorkflowNode, workflow_node, NodePort, NodeConfig
from ..node import WorkflowNode, workflow_node
@workflow_node('webhook_trigger')
class WebhookTriggerNode(WorkflowNode):
"""Webhook trigger node - triggers workflow via HTTP request"""
type_name = 'webhook_trigger'
category = 'trigger'
icon = 'Webhook'
name = 'webhook_trigger'
description = 'webhook_trigger'
name_zh = 'Webhook 触发'
name_en = 'Webhook Trigger'
description_zh = '通过 HTTP 请求触发工作流'
description_en = 'Trigger workflow via HTTP webhook'
inputs: ClassVar[list[NodePort]] = []
outputs: ClassVar[list[NodePort]] = []
config_schema: ClassVar[list[NodeConfig]] = []
async def execute(self, inputs: dict[str, Any], context: ExecutionContext) -> dict[str, Any]:
trigger_data = context.trigger_data