修复流程编排界面点击执行无效的问题
This commit is contained in:
parent
ffa30beaa8
commit
a392f71dd7
@ -24,7 +24,14 @@ export class FlowExecutor {
|
||||
* @returns {Promise<Object>} 执行结果
|
||||
*/
|
||||
async executeFlow(nodes, edges, initialData = {}) {
|
||||
console.group('[FlowExecutor] executeFlow START');
|
||||
console.log('Nodes count:', nodes?.length);
|
||||
console.log('Edges count:', edges?.length);
|
||||
console.log('Initial data:', initialData);
|
||||
|
||||
if (this.isExecuting) {
|
||||
console.error('[FlowExecutor] Flow already executing');
|
||||
console.groupEnd();
|
||||
throw new Error('流程正在执行中,请等待完成');
|
||||
}
|
||||
|
||||
@ -37,12 +44,20 @@ export class FlowExecutor {
|
||||
const executionNodes = JSON.parse(JSON.stringify(nodes));
|
||||
const executionEdges = JSON.parse(JSON.stringify(edges));
|
||||
|
||||
console.log('Execution nodes:', executionNodes.map(n => ({ id: n.id, type: n.type })));
|
||||
console.log('Execution edges:', executionEdges);
|
||||
|
||||
// 预构建执行图(一次性构建,避免重复计算)
|
||||
this.buildExecutionGraph(executionNodes, executionEdges);
|
||||
console.log('Execution order:', this.executionOrder);
|
||||
|
||||
// 执行流程
|
||||
const result = await this.executeGraph();
|
||||
|
||||
console.log('[FlowExecutor] executeFlow SUCCESS');
|
||||
console.log('Final results:', result);
|
||||
console.groupEnd();
|
||||
|
||||
return {
|
||||
success: true,
|
||||
result: result,
|
||||
@ -50,6 +65,9 @@ export class FlowExecutor {
|
||||
context: this.executionContext
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('[FlowExecutor] executeFlow FAILED:', error);
|
||||
console.error('Error stack:', error.stack);
|
||||
console.groupEnd();
|
||||
return {
|
||||
success: false,
|
||||
error: error.message,
|
||||
@ -172,6 +190,9 @@ export class FlowExecutor {
|
||||
* @returns {Promise<Object>} 执行结果
|
||||
*/
|
||||
async executeGraph() {
|
||||
console.log('[FlowExecutor] executeGraph START');
|
||||
console.log('Execution order:', this.executionOrder);
|
||||
|
||||
const context = {
|
||||
node_results: {},
|
||||
flow_id: this.executionContext.agent_name
|
||||
@ -183,14 +204,19 @@ export class FlowExecutor {
|
||||
// 按拓扑排序执行节点
|
||||
for (const nodeId of this.executionOrder) {
|
||||
const node = this.nodes.get(nodeId);
|
||||
console.log(`[FlowExecutor] Processing node: ${nodeId} (${node?.type})`);
|
||||
|
||||
// 检查节点是否应该被执行(O(1)时间复杂度)
|
||||
if (!this.shouldExecuteNode(node, context, executedNodes)) {
|
||||
console.log(`[FlowExecutor] Skipping node ${nodeId} (shouldExecuteNode=false)`);
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(`[FlowExecutor] Executing node ${nodeId}...`);
|
||||
// 执行节点
|
||||
const result = await this.executeNode(node, context);
|
||||
console.log(`[FlowExecutor] Node ${nodeId} result:`, result);
|
||||
|
||||
results[nodeId] = result;
|
||||
context.node_results[nodeId] = result;
|
||||
executedNodes.add(nodeId);
|
||||
@ -199,6 +225,7 @@ export class FlowExecutor {
|
||||
this.recordExecutionHistory(node, result);
|
||||
}
|
||||
|
||||
console.log('[FlowExecutor] executeGraph COMPLETE, results:', results);
|
||||
return results;
|
||||
}
|
||||
|
||||
@ -267,23 +294,44 @@ export class FlowExecutor {
|
||||
edges: this.edges
|
||||
};
|
||||
|
||||
const response = await fetch(`/api/action/jingrow.ai.nodes.${node.type}.${node.type}.execute`, {
|
||||
// Debug: log request details
|
||||
console.group(`[FlowExecutor] Executing node: ${node.type} (${node.id})`);
|
||||
console.log('Node data:', JSON.parse(JSON.stringify(node)));
|
||||
console.log('Node inputs:', nodeInputs);
|
||||
console.log('Node config:', config);
|
||||
console.log('Context:', JSON.parse(JSON.stringify(context)));
|
||||
console.log('Request URL:', `/jingrow/nodes/${node.type}/execute`);
|
||||
console.log('CSRF Token:', window.jingrow?.csrf_token);
|
||||
|
||||
const requestBody = JSON.stringify({
|
||||
context,
|
||||
inputs: nodeInputs,
|
||||
config
|
||||
});
|
||||
console.log('Request body size:', requestBody.length, 'bytes');
|
||||
|
||||
const response = await fetch(`/jingrow/nodes/${node.type}/execute`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Jingrow-CSRF-Token': window.jingrow?.csrf_token
|
||||
},
|
||||
body: JSON.stringify({
|
||||
context,
|
||||
inputs: nodeInputs,
|
||||
config
|
||||
})
|
||||
body: requestBody
|
||||
});
|
||||
|
||||
// Debug: log response details
|
||||
console.log('Response status:', response.status, response.statusText);
|
||||
console.log('Response headers:', Object.fromEntries(response.headers.entries()));
|
||||
|
||||
const resJson = await response.json();
|
||||
console.log('Response JSON:', resJson);
|
||||
console.groupEnd();
|
||||
|
||||
if (!resJson.message || resJson.message.success === false) {
|
||||
throw new Error(resJson.message?.error || '节点执行失败');
|
||||
const errorMsg = resJson.message?.error || resJson.error || '节点执行失败';
|
||||
console.error('[FlowExecutor] Node execution failed:', errorMsg);
|
||||
console.error('[FlowExecutor] Full response:', resJson);
|
||||
throw new Error(errorMsg);
|
||||
}
|
||||
|
||||
// 更新节点状态
|
||||
@ -294,6 +342,9 @@ export class FlowExecutor {
|
||||
return resJson.message;
|
||||
|
||||
} catch (error) {
|
||||
console.groupEnd();
|
||||
console.error('[FlowExecutor] Node execution error:', error);
|
||||
console.error('[FlowExecutor] Error stack:', error.stack);
|
||||
// 处理执行失败
|
||||
node.status = 'failed';
|
||||
node.error = error.message;
|
||||
|
||||
@ -108,8 +108,8 @@ export default defineConfig(({ mode, command }) => {
|
||||
allow: [appsDir]
|
||||
},
|
||||
proxy: {
|
||||
// jlocal 只代理 API 请求,静态资源由 vite/Caddy 直接托管
|
||||
'^/api': {
|
||||
// Proxy both /api and /jingrow routes to backend
|
||||
'^/(api|jingrow)': {
|
||||
target: BACKEND_URL,
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
|
||||
@ -133,6 +133,14 @@ def get_single(pagetype: str):
|
||||
return {'success': True, 'config': data}
|
||||
|
||||
|
||||
def get_single_value(pagetype: str, fieldname: str):
|
||||
"""获取 single 类型 pagetype 的单个字段值"""
|
||||
result = get_single(pagetype)
|
||||
if result.get('success') and result.get('config'):
|
||||
return result['config'].get(fieldname)
|
||||
return None
|
||||
|
||||
|
||||
def get_module_app(pagetype: str):
|
||||
"""获取指定 pagetype 的模块应用信息,返回后端适配器的原始结果结构。"""
|
||||
return get_page_instance(pagetype).get_module_app()
|
||||
|
||||
@ -24,7 +24,8 @@ async def execute_node(node_type: str, request: Request, request_data: Dict[str,
|
||||
executor = NodeExecutor()
|
||||
result = await executor.execute_node(node_type, flow_id, context, inputs, config, session_cookie)
|
||||
|
||||
return result
|
||||
# 统一响应格式,与 SaaS 版保持一致
|
||||
return {"message": result}
|
||||
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@ -4,10 +4,22 @@ Jingrow Cloud API 调用工具
|
||||
"""
|
||||
|
||||
import requests
|
||||
from jingrow.config import Config
|
||||
from functools import lru_cache
|
||||
from jingrow.utils.auth import get_jingrow_cloud_api_headers, get_jingrow_cloud_api_url
|
||||
|
||||
|
||||
@lru_cache(maxsize=1)
|
||||
def _get_cached_ai_settings():
|
||||
"""缓存 AI Settings 配置(含密码字段解密),避免重复请求"""
|
||||
from jingrow.utils.jingrow_api import get_ai_settings_from_jingrow
|
||||
return get_ai_settings_from_jingrow() or {}
|
||||
|
||||
|
||||
def clear_ai_settings_cache():
|
||||
"""清除 AI Settings 缓存,配置更新后调用"""
|
||||
_get_cached_ai_settings.cache_clear()
|
||||
|
||||
|
||||
def call_jingrow_model(prompt: str,
|
||||
image_urls: list = None,
|
||||
ai_temperature: float = None,
|
||||
@ -86,11 +98,12 @@ def call_chatgpt_model(prompt: str,
|
||||
model: str = None):
|
||||
"""调用 ChatGPT 模型"""
|
||||
try:
|
||||
api_url = getattr(Config, 'CHATGPT_API_URL', None) or "https://api.openai.com/v1/chat/completions"
|
||||
api_key = getattr(Config, 'CHATGPT_API_KEY', None)
|
||||
model_name = model or getattr(Config, 'CHATGPT_API_MODEL', None) or "gpt-4o"
|
||||
ai_settings = _get_cached_ai_settings()
|
||||
api_url = ai_settings.get('chatgpt_api_url') or "https://api.openai.com/v1/chat/completions"
|
||||
api_key = ai_settings.get('chatgpt_api_key')
|
||||
model_name = model or ai_settings.get('chatgpt_api_model') or "gpt-4o"
|
||||
if not api_key:
|
||||
return {"success": False, "error": "ChatGPT API 未配置"}
|
||||
return {"success": False, "error": "ChatGPT API 未配置,请在 Ai Settings 中设置 chatgpt_api_key"}
|
||||
content = [{"type": "text", "text": prompt}]
|
||||
if image_urls:
|
||||
for img in image_urls:
|
||||
@ -132,11 +145,12 @@ def call_deepseek_model(prompt: str,
|
||||
model: str = None):
|
||||
"""调用 DeepSeek 模型"""
|
||||
try:
|
||||
api_url = getattr(Config, 'DEEPSEEK_API_URL', None)
|
||||
api_key = getattr(Config, 'DEEPSEEK_API_KEY', None)
|
||||
model_name = model or getattr(Config, 'DEEPSEEK_API_MODEL', None) or "deepseek-chat"
|
||||
ai_settings = _get_cached_ai_settings()
|
||||
api_url = ai_settings.get('deepseek_api_url')
|
||||
api_key = ai_settings.get('deepseek_api_key')
|
||||
model_name = model or ai_settings.get('deepseek_api_model') or "deepseek-chat"
|
||||
if not api_url or not api_key:
|
||||
return {"success": False, "error": "DeepSeek API 未配置"}
|
||||
return {"success": False, "error": "DeepSeek API 未配置,请在 Ai Settings 中设置 deepseek_api_url 和 deepseek_api_key"}
|
||||
content = [{"type": "text", "text": prompt}]
|
||||
if image_urls:
|
||||
for img in image_urls:
|
||||
@ -178,11 +192,12 @@ def call_doubao_model(prompt: str,
|
||||
model: str = None):
|
||||
"""调用豆包模型"""
|
||||
try:
|
||||
api_url = getattr(Config, 'DOUBAO_API_URL', None) or getattr(Config, 'DOUBAO_API_URL'.upper(), None)
|
||||
api_key = getattr(Config, 'DOUBAO_API_KEY', None) or getattr(Config, 'DOUBAO_API_KEY'.upper(), None)
|
||||
model_name = model or getattr(Config, 'DOUBAO_API_MODEL', None) or "doubao-pro-32k-241215"
|
||||
ai_settings = _get_cached_ai_settings()
|
||||
api_url = ai_settings.get('doubao_api_url')
|
||||
api_key = ai_settings.get('doubao_api_key')
|
||||
model_name = model or ai_settings.get('doubao_api_model') or "doubao-pro-32k-241215"
|
||||
if not api_url or not api_key:
|
||||
return {"success": False, "error": "豆包 API 未配置"}
|
||||
return {"success": False, "error": "豆包 API 未配置,请在 Ai Settings 中设置 doubao_api_url 和 doubao_api_key"}
|
||||
content = [{"type": "text", "text": prompt}]
|
||||
if image_urls:
|
||||
for img in image_urls:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user