3 Commits

15 changed files with 51 additions and 47 deletions

View File

@@ -45,7 +45,7 @@ create table hudi_collect_build_b12.service_ai_flow_task
status enum ('ERROR','FINISHED','RUNNING') not null comment '任务运行状态', status enum ('ERROR','FINISHED','RUNNING') not null comment '任务运行状态',
template_description varchar(255) comment '任务对应的模板功能、内容说明', template_description varchar(255) comment '任务对应的模板功能、内容说明',
template_flow_graph longtext not null comment '任务对应的模板前端流程图数据', template_flow_graph longtext not null comment '任务对应的模板前端流程图数据',
template_input_schema longtext not null comment '任务对应的模板入参Schema', template_input_schema longtext comment '任务对应的模板入参Schema',
template_name varchar(255) not null comment '任务对应的模板名称', template_name varchar(255) not null comment '任务对应的模板名称',
primary key (id) primary key (id)
) comment ='流程任务记录' charset = utf8mb4; ) comment ='流程任务记录' charset = utf8mb4;
@@ -57,7 +57,7 @@ create table hudi_collect_build_b12.service_ai_flow_task_template
modified_time datetime(6) comment '记录更新时间', modified_time datetime(6) comment '记录更新时间',
description varchar(255) comment '模板功能、内容说明', description varchar(255) comment '模板功能、内容说明',
flow_graph longtext not null comment '前端流程图数据', flow_graph longtext not null comment '前端流程图数据',
input_schema longtext not null comment '模板入参Schema', input_schema longtext comment '模板入参Schema',
name varchar(255) not null comment '模板名称', name varchar(255) not null comment '模板名称',
primary key (id) primary key (id)
) comment ='流程任务模板' charset = utf8mb4; ) comment ='流程任务模板' charset = utf8mb4;

View File

@@ -1,5 +1,7 @@
package com.lanyuanxiaoyao.service.ai.web.controller.task; package com.lanyuanxiaoyao.service.ai.web.controller.task;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.lanyuanxiaoyao.service.ai.core.entity.amis.AmisResponse; import com.lanyuanxiaoyao.service.ai.core.entity.amis.AmisResponse;
@@ -14,6 +16,7 @@ import java.util.Map;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers; import org.mapstruct.factory.Mappers;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
@@ -39,12 +42,18 @@ public class TaskController extends SimpleControllerSupport<FlowTask, TaskContro
@GetMapping("input_data/{id}") @GetMapping("input_data/{id}")
public AmisResponse<?> getInputData(@PathVariable("id") Long id) throws JsonProcessingException { public AmisResponse<?> getInputData(@PathVariable("id") Long id) throws JsonProcessingException {
var task = flowTaskService.detailOrThrow(id); var task = flowTaskService.detailOrThrow(id);
if (ObjectUtil.isEmpty(task.getInput())) {
return AmisResponse.responseSuccess();
}
return AmisResponse.responseSuccess(mapper.readValue(task.getInput(), Map.class)); return AmisResponse.responseSuccess(mapper.readValue(task.getInput(), Map.class));
} }
@GetMapping("input_schema/{id}") @GetMapping("input_schema/{id}")
public AmisResponse<?> getInputSchema(@PathVariable("id") Long id) throws JsonProcessingException { public AmisResponse<?> getInputSchema(@PathVariable("id") Long id) throws JsonProcessingException {
var task = flowTaskService.detailOrThrow(id); var task = flowTaskService.detailOrThrow(id);
if (ObjectUtil.isEmpty(task.getTemplateInputSchema())) {
return AmisResponse.responseSuccess();
}
return AmisResponse.responseSuccess(mapper.readValue(task.getTemplateInputSchema(), Map.class)); return AmisResponse.responseSuccess(mapper.readValue(task.getTemplateInputSchema(), Map.class));
} }
@@ -63,7 +72,7 @@ public class TaskController extends SimpleControllerSupport<FlowTask, TaskContro
task.setTemplateDescription(template.getDescription()); task.setTemplateDescription(template.getDescription());
task.setTemplateInputSchema(template.getInputSchema()); task.setTemplateInputSchema(template.getInputSchema());
task.setTemplateFlowGraph(template.getFlowGraph()); task.setTemplateFlowGraph(template.getFlowGraph());
task.setInput(mapper.writeValueAsString(item.getInput())); task.setInput(ObjectUtil.isEmpty(item.getInput()) ? null : mapper.writeValueAsString(item.getInput()));
return task; return task;
}; };
} }
@@ -91,9 +100,13 @@ public class TaskController extends SimpleControllerSupport<FlowTask, TaskContro
public static class ListItem extends SimpleItem { public static class ListItem extends SimpleItem {
private String templateName; private String templateName;
private FlowTask.Status status; private FlowTask.Status status;
private Boolean hasInput;
@org.mapstruct.Mapper @org.mapstruct.Mapper(imports = {
StrUtil.class
})
public static abstract class Mapper { public static abstract class Mapper {
@Mapping(target = "hasInput", expression = "java(StrUtil.isNotBlank(task.getInput()))")
public abstract ListItem from(FlowTask task); public abstract ListItem from(FlowTask task);
} }
} }
@@ -104,8 +117,11 @@ public class TaskController extends SimpleControllerSupport<FlowTask, TaskContro
private String error; private String error;
private String result; private String result;
@org.mapstruct.Mapper @org.mapstruct.Mapper(imports = {
StrUtil.class
})
public static abstract class Mapper extends ListItem.Mapper { public static abstract class Mapper extends ListItem.Mapper {
@Mapping(target = "hasInput", expression = "java(StrUtil.isNotBlank(task.getInput()))")
public abstract DetailItem from(FlowTask task); public abstract DetailItem from(FlowTask task);
} }
} }

View File

@@ -39,6 +39,9 @@ public class TaskTemplateController extends SimpleControllerSupport<FlowTaskTemp
@GetMapping("input_schema/{id}") @GetMapping("input_schema/{id}")
public AmisResponse<?> getInputSchema(@PathVariable("id") Long id) throws JsonProcessingException { public AmisResponse<?> getInputSchema(@PathVariable("id") Long id) throws JsonProcessingException {
var template = flowTaskTemplateService.detailOrThrow(id); var template = flowTaskTemplateService.detailOrThrow(id);
if (ObjectUtil.isEmpty(template.getInputSchema())) {
return AmisResponse.responseSuccess();
}
return AmisResponse.responseSuccess(mapper.readValue(template.getInputSchema(), Map.class)); return AmisResponse.responseSuccess(mapper.readValue(template.getInputSchema(), Map.class));
} }

View File

@@ -1,6 +1,7 @@
package com.lanyuanxiaoyao.service.ai.web.engine.node; package com.lanyuanxiaoyao.service.ai.web.engine.node;
import com.lanyuanxiaoyao.service.ai.web.engine.FlowNodeRunner; import com.lanyuanxiaoyao.service.ai.web.engine.FlowNodeRunner;
import java.util.Map;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
/** /**
@@ -9,8 +10,16 @@ import lombok.extern.slf4j.Slf4j;
*/ */
@Slf4j @Slf4j
public class InputNode extends FlowNodeRunner { public class InputNode extends FlowNodeRunner {
public static final String KEY = "flow_inputs";
@Override @Override
public void run() { public void run() {
// 入参相关的内容在启动流程的时候已经注入了 var inputData = getContext().getData().get(KEY);
var inputs = this.<Map<String, Object>>getData("inputs");
for (String variable : inputs.keySet()) {
if (inputData.containsKey(variable)) {
setData(variable, inputData.get(variable));
}
}
} }
} }

View File

@@ -16,7 +16,6 @@ public class LlmNode extends FlowNodeRunner {
@Override @Override
public void run() { public void run() {
var variableMap = FlowHelper.generateInputVariablesMap(getNodeId(), getContext()); var variableMap = FlowHelper.generateInputVariablesMap(getNodeId(), getContext());
log.info("Variable map: {}", variableMap);
var sourcePrompt = (String) getData("systemPrompt"); var sourcePrompt = (String) getData("systemPrompt");
if (StrUtil.isNotBlank(sourcePrompt)) { if (StrUtil.isNotBlank(sourcePrompt)) {
var prompt = FlowHelper.renderTemplateText(sourcePrompt, variableMap.toMap()); var prompt = FlowHelper.renderTemplateText(sourcePrompt, variableMap.toMap());

View File

@@ -10,10 +10,11 @@ import lombok.extern.slf4j.Slf4j;
*/ */
@Slf4j @Slf4j
public class OutputNode extends FlowNodeRunner { public class OutputNode extends FlowNodeRunner {
private static final String KEY = "flow_outputs";
@Override @Override
public void run() { public void run() {
String expression = getData("output"); var variableMap = FlowHelper.generateInputVariablesMap(getNodeId(), getContext());
var targetVariable = FlowHelper.generateVariable(expression, getContext()); getContext().getData().put(KEY, variableMap.toMap());
log.info("Target: {}", targetVariable);
} }
} }

View File

@@ -32,7 +32,7 @@ public class FlowTask extends SimpleEntity {
@Comment("任务对应的模板功能、内容说明") @Comment("任务对应的模板功能、内容说明")
private String templateDescription; private String templateDescription;
@Comment("任务对应的模板入参Schema") @Comment("任务对应的模板入参Schema")
@Column(nullable = false, columnDefinition = "longtext") @Column(columnDefinition = "longtext")
private String templateInputSchema; private String templateInputSchema;
@Comment("任务对应的模板前端流程图数据") @Comment("任务对应的模板前端流程图数据")
@Column(nullable = false, columnDefinition = "longtext") @Column(nullable = false, columnDefinition = "longtext")

View File

@@ -28,7 +28,7 @@ public class FlowTaskTemplate extends SimpleEntity {
@Comment("模板功能、内容说明") @Comment("模板功能、内容说明")
private String description; private String description;
@Comment("模板入参Schema") @Comment("模板入参Schema")
@Column(nullable = false, columnDefinition = "longtext") @Column(columnDefinition = "longtext")
private String inputSchema; private String inputSchema;
@Comment("前端流程图数据") @Comment("前端流程图数据")
@Column(nullable = false, columnDefinition = "longtext") @Column(nullable = false, columnDefinition = "longtext")

View File

@@ -2,7 +2,6 @@ package com.lanyuanxiaoyao.service.ai.web.service.task;
import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
@@ -57,15 +56,8 @@ public class FlowTaskService extends SimpleServiceSupport<FlowTask> {
FlowContext context = new FlowContext(); FlowContext context = new FlowContext();
context.setData(graphVo.getData()); context.setData(graphVo.getData());
// 如果发现输入节点,将相关数据注入到对应节点的上下文中 if (ObjectUtil.isNotEmpty(flowTask.getInput())) {
var inputNode = flowGraph.nodes() context.getData().put(InputNode.KEY, mapper.readValue(flowTask.getInput(), new TypeReference<>() {
.detectOptional(node -> StrUtil.equals(node.type(), "input-node") && ObjectUtil.isEmpty(node.parentId()))
.orElse(null);
if (ObjectUtil.isNotNull(inputNode)) {
if (!context.getData().containsKey(inputNode.id())) {
context.getData().put(inputNode.id(), Maps.mutable.empty());
}
context.getData().get(inputNode.id()).putAll(mapper.readValue(flowTask.getInput(), new TypeReference<>() {
})); }));
} }

View File

@@ -38,8 +38,6 @@ public class FlowTaskTemplateService extends SimpleServiceSupport<FlowTaskTempla
if (ObjectUtil.isNotNull(nodeData) && nodeData.containsKey("inputs")) { if (ObjectUtil.isNotNull(nodeData) && nodeData.containsKey("inputs")) {
template.setInputSchema(mapper.writeValueAsString(nodeData.get("inputs"))); template.setInputSchema(mapper.writeValueAsString(nodeData.get("inputs")));
} }
} else {
template.setInputSchema("{}");
} }
template.setFlowGraph(flowGraph); template.setFlowGraph(flowGraph);

View File

@@ -94,7 +94,7 @@ function FlowEditor(props: FlowEditorProps) {
onConnect(connection) onConnect(connection)
} catch (e) { } catch (e) {
// @ts-ignore // @ts-ignore
messageApi.error(e.toString()) message.error(e.toString())
} }
}} }}
// @ts-ignore // @ts-ignore

View File

@@ -1,31 +1,16 @@
import type {NodeProps} from '@xyflow/react' import type {NodeProps} from '@xyflow/react'
import React, {useCallback} from 'react' import React, {useCallback} from 'react'
import {generateAllIncomerOutputVariablesFormOptions} from '../Helper.tsx'
import {useDataStore} from '../store/DataStore.ts' import {useDataStore} from '../store/DataStore.ts'
import {useFlowStore} from '../store/FlowStore.ts' import {useFlowStore} from '../store/FlowStore.ts'
import AmisNode, {EndNodeHandler, nodeClassName} from './AmisNode.tsx'
import type {FormSchema} from '../types.ts' import type {FormSchema} from '../types.ts'
import AmisNode, {EndNodeHandler, inputsFormColumns, nodeClassName} from './AmisNode.tsx'
const OutputNode = (props: NodeProps) => { const OutputNode = (props: NodeProps) => {
const {getNodes, getEdges} = useFlowStore() const {getNodes, getEdges} = useFlowStore()
const {getData} = useDataStore() const {getData} = useDataStore()
const formSchema: () => FormSchema = useCallback(() => ({ const formSchema: () => FormSchema = useCallback(() => ({
columns: [ columns: inputsFormColumns(props.id, getNodes(), getEdges(), getData()),
{
type: 'select',
name: 'output',
label: '输出变量',
required: true,
selectMode: 'group',
options: generateAllIncomerOutputVariablesFormOptions(
props.id,
getNodes(),
getEdges(),
getData(),
),
},
]
}), [props.id]) }), [props.id])
return ( return (

File diff suppressed because one or more lines are too long

View File

@@ -87,6 +87,7 @@ const FlowTask: React.FC = () => {
width: 200, width: 200,
buttons: [ buttons: [
{ {
visibleOn: 'hasInput',
type: 'action', type: 'action',
label: '查看', label: '查看',
level: 'link', level: 'link',
@@ -107,17 +108,18 @@ const FlowTask: React.FC = () => {
return { return {
...payload, ...payload,
data: { data: {
...generateInputForm(payload.data, undefined, false, true), ...generateInputForm(payload.data ?? {}, undefined, false, true),
id: 'db8a4d10-0c47-4e27-b1a4-d0f2e1c15992', id: 'db8a4d10-0c47-4e27-b1a4-d0f2e1c15992',
initApi: { initApi: {
method: 'get', method: 'get',
url: `${commonInfo.baseAiUrl}/flow_task/input_data/\${id}`, url: `${commonInfo.baseAiUrl}/flow_task/input_data/\${id}`,
// @ts-ignore // @ts-ignore
adaptor: (payload, response, api, context) => { adaptor: (payload, response, api, context) => {
console.log(payload)
return { return {
...payload, ...payload,
data: { data: {
inputData: payload.data, inputData: payload.data ?? {},
}, },
} }
}, },

View File

@@ -159,7 +159,7 @@ const FlowTaskAdd: React.FC = () => {
input: '${inputData|default:undefined}', input: '${inputData|default:undefined}',
} }
}, },
...generateInputForm(payload.data, undefined, false), ...generateInputForm(payload.data ?? {}, undefined, false),
}, },
} }
}, },