Build lightweight AI agent admin

This commit is contained in:
Codex
2026-06-08 18:14:59 +08:00
commit e164840f43
2530 changed files with 435693 additions and 0 deletions
@@ -0,0 +1,155 @@
"""
开始节点
"""
import logging
from typing import Any, Dict, List
from ..base import BaseNode, NodeContext, NodeResult
from ..registry import NodeRegistry
logger = logging.getLogger(__name__)
@NodeRegistry.register
class StartNode(BaseNode):
"""
开始节点
工作流的入口节点,接收用户输入
"""
node_type = 'start'
node_name = '开始'
node_category = 'basic'
node_icon = 'play-circle'
node_description = '工作流的开始节点,接收用户输入'
inputs = []
outputs = [
{
'name': 'user_input',
'type': 'string',
'description': '用户输入',
},
{
'name': 'application_id',
'type': 'string',
'description': '子应用ID(在子应用模式下自动注入)',
},
{
'name': 'application_code',
'type': 'string',
'description': '子应用编码(在子应用模式下自动注入)',
},
{
'name': 'form_code',
'type': 'string',
'description': '表单编码(从表单列表调用时自动注入)',
},
]
def execute(self, context: NodeContext) -> NodeResult:
"""执行开始节点"""
output_variables = {}
# 调试日志
logger.info(f"StartNode execute - context.variables: {list(context.variables.keys())}")
logger.info(f"StartNode execute - application_id in variables: {'application_id' in context.variables}")
if 'application_id' in context.variables:
logger.info(f"StartNode execute - application_id value: {context.variables['application_id']}")
# 首先将 user_input 作为开始节点的输出变量
# 这样下游节点可以通过 {{start-xxx.user_input}} 引用
if context.user_input:
output_variables['user_input'] = context.user_input
elif 'user_input' in context.variables:
output_variables['user_input'] = context.variables['user_input']
# 将 application_id 作为开始节点的输出变量
# 这样下游节点可以通过 {{start-xxx.application_id}} 引用
# 主应用模式下为空字符串,子应用模式下为实际的应用ID
output_variables['application_id'] = context.variables.get('application_id', 'main')
# 将 application_code 作为开始节点的输出变量
# 这样下游节点可以通过 {{start-xxx.application_code}} 引用
# 主应用模式下为空字符串,子应用模式下为实际的应用编码
output_variables['application_code'] = context.variables.get('application_code', '')
# 将 form_code 作为开始节点的输出变量
# 这样下游节点可以通过 {{start-xxx.form_code}} 引用
# 从表单列表调用时会自动注入,否则为空字符串
output_variables['form_code'] = context.variables.get('form_code', '')
logger.info(f"StartNode execute - output_variables: {output_variables}")
# 处理前端定义的变量(variables 数组)
# 格式: [{ variable: 'name', type: 'string', label: '名称', required: true, default_value: '' }]
variables = self.config.get('variables', [])
for var in variables:
var_name = var.get('variable', '')
if not var_name:
continue
default_value = var.get('default_value', '')
var_type = var.get('type', 'string')
# 如果 context 中已有该变量(从 inputs 传入),使用传入的值
# 否则使用默认值
if var_name in context.variables and context.variables[var_name]:
value = context.variables[var_name]
else:
value = default_value
# 类型转换
if var_type == 'number' and value:
try:
value = float(value) if '.' in str(value) else int(value)
except (ValueError, TypeError):
pass
elif var_type == 'boolean':
if isinstance(value, str):
value = value.lower() in ('true', '1', 'yes')
context.set_variable(var_name, value)
output_variables[var_name] = value
# 处理自定义输入变量(兼容旧格式 input_variables
input_variables = self.config.get('input_variables', [])
for var in input_variables:
var_name = var.get('name', '')
var_value = var.get('default', '')
if var_name:
# 如果 context 中没有该变量,使用默认值
if var_name not in context.variables or not context.variables[var_name]:
context.set_variable(var_name, var_value)
output_variables[var_name] = context.get_variable(var_name)
return NodeResult(
success=True,
output=output_variables,
output_variables=output_variables,
)
@classmethod
def get_config_schema(cls) -> Dict[str, Any]:
"""获取配置 Schema"""
return {
'type': 'object',
'properties': {
'input_variables': {
'type': 'array',
'title': '输入变量',
'items': {
'type': 'object',
'properties': {
'name': {'type': 'string', 'title': '变量名'},
'type': {'type': 'string', 'title': '类型', 'enum': ['string', 'number', 'boolean']},
'description': {'type': 'string', 'title': '描述'},
'default': {'type': 'string', 'title': '默认值'},
'required': {'type': 'boolean', 'title': '是否必填'},
},
},
},
},
}