mirror of
https://github.com/slhaf/Partner.git
synced 2026-05-12 16:53:04 +08:00
refactor(action): pass launcher through meta action flow instead of inferring command by file extension
This commit is contained in:
@@ -329,7 +329,8 @@ public class ActionCore extends PartnerCore<ActionCore> {
|
|||||||
MetaAction.Type type = BUILTIN_LOCATION.equals(split[0]) ? MetaAction.Type.BUILTIN : MetaAction.Type.MCP;
|
MetaAction.Type type = BUILTIN_LOCATION.equals(split[0]) ? MetaAction.Type.BUILTIN : MetaAction.Type.MCP;
|
||||||
return new MetaAction(
|
return new MetaAction(
|
||||||
split[1],
|
split[1],
|
||||||
metaActionInfo.isIo(),
|
metaActionInfo.getIo(),
|
||||||
|
metaActionInfo.getLauncher(),
|
||||||
type,
|
type,
|
||||||
split[0]
|
split[0]
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ public class GeneratedData {
|
|||||||
private List<String> dependencies;
|
private List<String> dependencies;
|
||||||
private String code;
|
private String code;
|
||||||
private String codeType;
|
private String codeType;
|
||||||
|
private String launcher;
|
||||||
private boolean serialize;
|
private boolean serialize;
|
||||||
private JSONObject responseSchema;
|
private JSONObject responseSchema;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,9 +13,13 @@ data class MetaAction(
|
|||||||
* 是否IO密集,用于决定使用何种线程池
|
* 是否IO密集,用于决定使用何种线程池
|
||||||
*/
|
*/
|
||||||
val io: Boolean = false,
|
val io: Boolean = false,
|
||||||
|
/**
|
||||||
|
* 启动器/解释器,对于原生 MCP Tool 、Dynamic Action 来说可忽略,目前仅用于 ORIGIN 类型
|
||||||
|
*/
|
||||||
|
val launcher: String? = null,
|
||||||
/**
|
/**
|
||||||
* 行动程序类型,可分为 MCP、ORIGIN、BUILTIN 三种,
|
* 行动程序类型,可分为 MCP、ORIGIN、BUILTIN 三种,
|
||||||
* 分别对应读取到的 MCP Tool、生成的临时行动程序、本地内置行动
|
* 分别对应读取到的 MCP Tool、生成的临时行动程序、内置行动
|
||||||
*/
|
*/
|
||||||
val type: Type,
|
val type: Type,
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,25 +1,44 @@
|
|||||||
package work.slhaf.partner.core.action.entity;
|
package work.slhaf.partner.core.action.entity
|
||||||
|
|
||||||
import com.alibaba.fastjson2.JSONObject;
|
import com.alibaba.fastjson2.JSONObject
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
@Data
|
data class MetaActionInfo(
|
||||||
public class MetaActionInfo {
|
/**
|
||||||
private boolean io;
|
* 是否 IO 密集
|
||||||
|
*/
|
||||||
|
val io: Boolean,
|
||||||
|
/**
|
||||||
|
* 所需的启动器/解释器
|
||||||
|
*/
|
||||||
|
val launcher: String?,
|
||||||
|
/**
|
||||||
|
* 参数描述
|
||||||
|
*/
|
||||||
|
val params: Map<String, String>,
|
||||||
|
/**
|
||||||
|
* 行动功能描述
|
||||||
|
*/
|
||||||
|
val description: String,
|
||||||
|
/**
|
||||||
|
* 行动标签
|
||||||
|
*/
|
||||||
|
val tags: Set<String>,
|
||||||
|
/**
|
||||||
|
* 前置行动依赖
|
||||||
|
*/
|
||||||
|
val preActions: Set<String>,
|
||||||
|
/**
|
||||||
|
* 后置行动依赖
|
||||||
|
*/
|
||||||
|
val postActions: Set<String>,
|
||||||
|
|
||||||
private Map<String, Object> params;
|
|
||||||
private String description;
|
|
||||||
private List<String> tags;
|
|
||||||
|
|
||||||
private List<String> preActions;
|
|
||||||
private List<String> postActions;
|
|
||||||
/**
|
/**
|
||||||
* 是否严格依赖前置行动的成功执行,若为true且前置行动失败则不执行该行动,后置任务多为触发式。默认即执行。
|
* 是否严格依赖前置行动的成功执行,若为true且前置行动失败则不执行该行动,后置任务多为触发式。默认即执行。
|
||||||
*/
|
*/
|
||||||
private boolean strictDependencies;
|
val strictDependencies: Boolean,
|
||||||
|
/**
|
||||||
private JSONObject responseSchema;
|
* 响应格式说明
|
||||||
}
|
*/
|
||||||
|
val responseSchema: JSONObject,
|
||||||
|
)
|
||||||
|
|||||||
@@ -11,18 +11,10 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||||||
|
|
||||||
public class CommandExecutionService {
|
public class CommandExecutionService {
|
||||||
|
|
||||||
public String[] buildCommands(String ext, Map<String, Object> params, String absolutePath) {
|
public String[] buildCommands(String launcher, Map<String, Object> params, String absolutePath) {
|
||||||
String command = switch (ext) {
|
|
||||||
case "py" -> "python";
|
|
||||||
case "sh" -> "bash";
|
|
||||||
default -> null;
|
|
||||||
};
|
|
||||||
if (command == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
int paramSize = params == null ? 0 : params.size();
|
int paramSize = params == null ? 0 : params.size();
|
||||||
String[] commands = new String[paramSize + 2];
|
String[] commands = new String[paramSize + 2];
|
||||||
commands[0] = command;
|
commands[0] = launcher;
|
||||||
commands[1] = absolutePath;
|
commands[1] = absolutePath;
|
||||||
AtomicInteger paramCount = new AtomicInteger(2);
|
AtomicInteger paramCount = new AtomicInteger(2);
|
||||||
if (params != null) {
|
if (params != null) {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package work.slhaf.partner.core.action.runner.execution;
|
package work.slhaf.partner.core.action.runner.execution;
|
||||||
|
|
||||||
import cn.hutool.core.io.FileUtil;
|
|
||||||
import work.slhaf.partner.core.action.entity.MetaAction;
|
import work.slhaf.partner.core.action.entity.MetaAction;
|
||||||
import work.slhaf.partner.core.action.runner.RunnerClient;
|
import work.slhaf.partner.core.action.runner.RunnerClient;
|
||||||
|
|
||||||
@@ -17,18 +16,7 @@ public class OriginExecutionService {
|
|||||||
public RunnerClient.RunnerResponse run(MetaAction metaAction) {
|
public RunnerClient.RunnerResponse run(MetaAction metaAction) {
|
||||||
RunnerClient.RunnerResponse response = new RunnerClient.RunnerResponse();
|
RunnerClient.RunnerResponse response = new RunnerClient.RunnerResponse();
|
||||||
File file = new File(metaAction.getLocation());
|
File file = new File(metaAction.getLocation());
|
||||||
String ext = FileUtil.getSuffix(file);
|
String[] commands = commandExecutionService.buildCommands(metaAction.getLauncher(), metaAction.getParams(), file.getAbsolutePath());
|
||||||
if (ext == null || ext.isEmpty()) {
|
|
||||||
response.setOk(false);
|
|
||||||
response.setData("未知文件类型");
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
String[] commands = commandExecutionService.buildCommands(ext, metaAction.getParams(), file.getAbsolutePath());
|
|
||||||
if (commands == null || commands.length == 0) {
|
|
||||||
response.setOk(false);
|
|
||||||
response.setData("不支持的文件类型: " + file.getName());
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
CommandExecutionService.Result execResult = commandExecutionService.exec(commands);
|
CommandExecutionService.Result execResult = commandExecutionService.exec(commands);
|
||||||
response.setOk(execResult.isOk());
|
response.setOk(execResult.isOk());
|
||||||
response.setData(execResult.getTotal());
|
response.setData(execResult.getTotal());
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package work.slhaf.partner.core.action.runner.mcp;
|
package work.slhaf.partner.core.action.runner.mcp;
|
||||||
|
|
||||||
import cn.hutool.core.io.FileUtil;
|
|
||||||
import cn.hutool.json.JSONUtil;
|
import cn.hutool.json.JSONUtil;
|
||||||
import com.alibaba.fastjson2.JSONObject;
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
import io.modelcontextprotocol.common.McpTransportContext;
|
import io.modelcontextprotocol.common.McpTransportContext;
|
||||||
@@ -292,8 +291,8 @@ public class DynamicActionMcpManager implements AutoCloseable {
|
|||||||
Map<String, Object> additional = Map.of(
|
Map<String, Object> additional = Map.of(
|
||||||
"pre", info.getPreActions(),
|
"pre", info.getPreActions(),
|
||||||
"post", info.getPostActions(),
|
"post", info.getPostActions(),
|
||||||
"strict_pre", info.isStrictDependencies(),
|
"strict_pre", info.getStrictDependencies(),
|
||||||
"io", info.isIo()
|
"io", info.getIo()
|
||||||
);
|
);
|
||||||
McpSchema.Tool tool = McpSchema.Tool.builder()
|
McpSchema.Tool tool = McpSchema.Tool.builder()
|
||||||
.name(name)
|
.name(name)
|
||||||
@@ -305,18 +304,17 @@ public class DynamicActionMcpManager implements AutoCloseable {
|
|||||||
.build();
|
.build();
|
||||||
return McpStatelessServerFeatures.AsyncToolSpecification.builder()
|
return McpStatelessServerFeatures.AsyncToolSpecification.builder()
|
||||||
.tool(tool)
|
.tool(tool)
|
||||||
.callHandler(buildToolHandler(program))
|
.callHandler(buildToolHandler(program, info.getLauncher()))
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private BiFunction<McpTransportContext, McpSchema.CallToolRequest, Mono<McpSchema.CallToolResult>> buildToolHandler(File program) {
|
private BiFunction<McpTransportContext, McpSchema.CallToolRequest, Mono<McpSchema.CallToolResult>> buildToolHandler(File program, String launcher) {
|
||||||
return (mcpTransportContext, callToolRequest) -> {
|
return (mcpTransportContext, callToolRequest) -> {
|
||||||
Map<String, Object> arguments = callToolRequest.arguments();
|
Map<String, Object> arguments = callToolRequest.arguments();
|
||||||
if (arguments == null) {
|
if (arguments == null) {
|
||||||
arguments = Map.of();
|
arguments = Map.of();
|
||||||
}
|
}
|
||||||
String ext = FileUtil.getSuffix(program);
|
String[] commands = commandExecutionService.buildCommands(launcher, arguments, program.getAbsolutePath());
|
||||||
String[] commands = commandExecutionService.buildCommands(ext, arguments, program.getAbsolutePath());
|
|
||||||
if (commands == null) {
|
if (commands == null) {
|
||||||
return Mono.just(McpSchema.CallToolResult.builder()
|
return Mono.just(McpSchema.CallToolResult.builder()
|
||||||
.addTextContent("未知文件类型: " + program.getName())
|
.addTextContent("未知文件类型: " + program.getName())
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ public class DynamicActionGenerator extends AbstractAgentModule.Sub<GeneratorInp
|
|||||||
MetaAction tempAction = new MetaAction(
|
MetaAction tempAction = new MetaAction(
|
||||||
input.getActionName(),
|
input.getActionName(),
|
||||||
true,
|
true,
|
||||||
|
generatorData.getLauncher(),
|
||||||
MetaAction.Type.ORIGIN,
|
MetaAction.Type.ORIGIN,
|
||||||
location
|
location
|
||||||
);
|
);
|
||||||
@@ -63,6 +64,7 @@ public class DynamicActionGenerator extends AbstractAgentModule.Sub<GeneratorInp
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO persist serialize. For now, it can be scheduled as a cycling-triggered StateAction or as a special MetaAction
|
||||||
private void waitingSerialize() {
|
private void waitingSerialize() {
|
||||||
throw new UnsupportedOperationException("Unimplemented method 'waitingSerialize'");
|
throw new UnsupportedOperationException("Unimplemented method 'waitingSerialize'");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ public class RepairerInput {
|
|||||||
|
|
||||||
private String userId;
|
private String userId;
|
||||||
private List<Message> recentMessages;
|
private List<Message> recentMessages;
|
||||||
private Map<String, Object> params;
|
private Map<String, String> params;
|
||||||
private String actionDescription;
|
private String actionDescription;
|
||||||
private List<HistoryAction> historyActionResults;
|
private List<HistoryAction> historyActionResults;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -380,12 +380,12 @@ public class ActionPlanner extends AbstractAgentModule.Running<PartnerRunningFlo
|
|||||||
for (String actionKey : actionKeys) {
|
for (String actionKey : actionKeys) {
|
||||||
// 根据 actionKey 加载行动信息,并检查是否存在必需前置依赖
|
// 根据 actionKey 加载行动信息,并检查是否存在必需前置依赖
|
||||||
MetaActionInfo metaActionInfo = actionCapability.loadMetaActionInfo(actionKey);
|
MetaActionInfo metaActionInfo = actionCapability.loadMetaActionInfo(actionKey);
|
||||||
List<String> preActions = metaActionInfo.getPreActions();
|
Set<String> preActions = metaActionInfo.getPreActions();
|
||||||
boolean preActionsExist = preActions != null && !preActions.isEmpty();
|
boolean preActionsExist = preActions.isEmpty();
|
||||||
if (!preActionsExist) {
|
if (!preActionsExist) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!metaActionInfo.isStrictDependencies()) {
|
if (!metaActionInfo.getStrictDependencies()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (checkDependenciesExist(lastOrder, preActions, primaryActionChain)) {
|
if (checkDependenciesExist(lastOrder, preActions, primaryActionChain)) {
|
||||||
@@ -398,8 +398,8 @@ public class ActionPlanner extends AbstractAgentModule.Running<PartnerRunningFlo
|
|||||||
fixed.set(true);
|
fixed.set(true);
|
||||||
List<String> actionsInChain = primaryActionChain.computeIfAbsent(lastOrder,
|
List<String> actionsInChain = primaryActionChain.computeIfAbsent(lastOrder,
|
||||||
list -> new ArrayList<>());
|
list -> new ArrayList<>());
|
||||||
preActions = new ArrayList<>(preActions);
|
preActions = new HashSet<>(preActions);
|
||||||
preActions.removeAll(actionsInChain);
|
actionsInChain.forEach(preActions::remove);
|
||||||
actionsInChain.addAll(preActions);
|
actionsInChain.addAll(preActions);
|
||||||
tempOrders.add(lastOrder);
|
tempOrders.add(lastOrder);
|
||||||
}
|
}
|
||||||
@@ -418,7 +418,7 @@ public class ActionPlanner extends AbstractAgentModule.Running<PartnerRunningFlo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkDependenciesExist(int lastOrder, List<String> preActions,
|
private boolean checkDependenciesExist(int lastOrder, Set<String> preActions,
|
||||||
Map<Integer, List<String>> primaryActionChain) {
|
Map<Integer, List<String>> primaryActionChain) {
|
||||||
if (!primaryActionChain.containsKey(lastOrder)) {
|
if (!primaryActionChain.containsKey(lastOrder)) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
Reference in New Issue
Block a user