feat: restore source parity and harden agent runtime

This commit is contained in:
2026-06-22 11:17:26 +08:00
parent e33f08277b
commit 0793eb82d6
596 changed files with 168879 additions and 290 deletions
@@ -0,0 +1,133 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
服务调用节点处理器
处理外部 HTTP 服务调用
"""
import json
import logging
import re
from typing import Dict, Optional, TYPE_CHECKING
import httpx
from online_dev.workflow.engine.handlers.base_handler import BaseNodeHandler
if TYPE_CHECKING:
from online_dev.workflow.engine.base import ExecutionContext
logger = logging.getLogger(__name__)
class ServiceHandler(BaseNodeHandler):
"""
服务调用节点处理器
支持:
- HTTP 方法: GET/POST/PUT/DELETE/PATCH
- 请求头配置
- 参数变量替换
- 重试机制
- 失败处理策略
"""
async def execute(self, context: 'ExecutionContext', node: Dict) -> None:
"""
执行服务调用节点
"""
node_id = self.get_node_id(node)
node_name = self.get_node_name(node)
node_config = self.get_node_config(node)
url = node_config.get('url', '')
method = node_config.get('method', 'POST')
headers = {h['key']: h['value'] for h in node_config.get('headers', []) if h.get('key')}
params = node_config.get('params', '')
body = node_config.get('body', '')
timeout = node_config.get('timeout', 30)
retry_count = node_config.get('retryCount', 0)
fail_action = node_config.get('failAction', 'stop')
result_variable = node_config.get('resultVariable', '')
logger.info(f"服务调用节点 - {method} {url}")
# 更新实例当前节点
await self.update_instance_node(context, node)
success = False
response_data = None
error_message = ''
# 尝试调用服务
async with httpx.AsyncClient() as client:
for attempt in range(retry_count + 1):
try:
# 解析参数和请求体(支持变量替换)
parsed_params = self._parse_params(params, context.form_data)
parsed_body = self._parse_params(body, context.form_data)
response = await client.request(
method=method,
url=url,
headers=headers,
params=parsed_params if method == 'GET' else None,
json=parsed_body if method != 'GET' and parsed_body else None,
timeout=timeout,
)
response.raise_for_status()
response_data = response.json() if response.text else {}
success = True
break
except Exception as e:
error_message = str(e)
logger.warning(f"服务调用失败 (尝试 {attempt + 1}/{retry_count + 1}): {e}")
# 记录日志
await self.create_log(
context, node, 'service_call',
comment=f"{'成功' if success else '失败'}: {method} {url}",
extra_data={
'url': url,
'method': method,
'success': success,
'response': response_data,
'error': error_message,
},
)
if success:
# 存储结果到流程变量
if result_variable and response_data:
context.form_data[result_variable] = response_data
await self.advance_to_next(context, node)
else:
# 处理失败
if fail_action == 'continue':
logger.warning(f"服务调用失败,继续流程")
await self.advance_to_next(context, node)
elif fail_action == 'stop':
logger.error(f"服务调用失败,终止流程")
await self.engine._end_instance(context, 'rejected')
def _parse_params(self, params_str: str, form_data: Dict) -> Optional[Dict]:
"""
解析服务调用参数,支持变量替换
变量格式: ${field_name}
"""
if not params_str:
return None
# 替换变量
def replace_var(match):
var_name = match.group(1)
return str(form_data.get(var_name, ''))
replaced = re.sub(r'\$\{(\w+)\}', replace_var, params_str)
try:
return json.loads(replaced)
except json.JSONDecodeError:
return None