mirror of
https://github.com/slhaf/Partner.git
synced 2026-05-12 08:43:02 +08:00
refactor(action): support built-in actions
This commit is contained in:
19
.idea/misc.xml
generated
19
.idea/misc.xml
generated
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="EntryPointsManager">
|
||||
<list size="15">
|
||||
<list size="16">
|
||||
<item index="0" class="java.lang.String" itemvalue="lombok.Data" />
|
||||
<item index="1" class="java.lang.String" itemvalue="net.bytebuddy.implementation.bind.annotation.RuntimeType" />
|
||||
<item index="2" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.capability.annotation.Capability" />
|
||||
@@ -9,14 +9,15 @@
|
||||
<item index="4" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.capability.annotation.CapabilityMethod" />
|
||||
<item index="5" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.capability.annotation.CoordinateManager" />
|
||||
<item index="6" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.capability.annotation.Coordinated" />
|
||||
<item index="7" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.module.annotation.AfterExecute" />
|
||||
<item index="8" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.module.annotation.AgentRunningModule" />
|
||||
<item index="9" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.module.annotation.AgentSubModule" />
|
||||
<item index="10" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.module.annotation.BeforeExecute" />
|
||||
<item index="11" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.module.annotation.Init" />
|
||||
<item index="12" class="java.lang.String" itemvalue="work.slhaf.partner.api.capability.annotation.CapabilityMethod" />
|
||||
<item index="13" class="java.lang.String" itemvalue="work.slhaf.partner.api.capability.annotation.CoordinateManager" />
|
||||
<item index="14" class="java.lang.String" itemvalue="work.slhaf.partner.api.register.capability.annotation.Capability" />
|
||||
<item index="7" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.component.annotation.Init" />
|
||||
<item index="8" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.module.annotation.AfterExecute" />
|
||||
<item index="9" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.module.annotation.AgentRunningModule" />
|
||||
<item index="10" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.module.annotation.AgentSubModule" />
|
||||
<item index="11" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.module.annotation.BeforeExecute" />
|
||||
<item index="12" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.module.annotation.Init" />
|
||||
<item index="13" class="java.lang.String" itemvalue="work.slhaf.partner.api.capability.annotation.CapabilityMethod" />
|
||||
<item index="14" class="java.lang.String" itemvalue="work.slhaf.partner.api.capability.annotation.CoordinateManager" />
|
||||
<item index="15" class="java.lang.String" itemvalue="work.slhaf.partner.api.register.capability.annotation.Capability" />
|
||||
</list>
|
||||
<writeAnnotations>
|
||||
<writeAnnotation name="work.slhaf.partner.api.agent.factory.capability.annotation.InjectCapability" />
|
||||
|
||||
@@ -53,6 +53,8 @@ public interface ActionCapability {
|
||||
|
||||
MetaActionInfo loadMetaActionInfo(@NonNull String actionKey);
|
||||
|
||||
void registerMetaActions(@NonNull Map<String, MetaActionInfo> metaActions);
|
||||
|
||||
Map<String, MetaActionInfo> listAvailableMetaActions();
|
||||
|
||||
boolean checkExists(String... actionKeys);
|
||||
|
||||
@@ -30,6 +30,7 @@ import java.util.stream.Collectors;
|
||||
@CapabilityCore(value = "action")
|
||||
@Slf4j
|
||||
public class ActionCore extends PartnerCore<ActionCore> {
|
||||
public static final String BUILTIN_LOCATION = "builtin";
|
||||
|
||||
private final Lock cacheLock = new ReentrantLock();
|
||||
// 由于当前的执行器逻辑实现,平台线程池大小不得小于 2,这里规定为最小为 4
|
||||
@@ -273,7 +274,12 @@ public class ActionCore extends PartnerCore<ActionCore> {
|
||||
}
|
||||
|
||||
@CapabilityMethod
|
||||
public Map<String, MetaActionInfo> listAvailableActions() {
|
||||
public void registerMetaActions(@NonNull Map<String, MetaActionInfo> metaActions) {
|
||||
existedMetaActions.putAll(metaActions);
|
||||
}
|
||||
|
||||
@CapabilityMethod
|
||||
public Map<String, MetaActionInfo> listAvailableMetaActions() {
|
||||
return existedMetaActions;
|
||||
}
|
||||
|
||||
@@ -320,10 +326,11 @@ public class ActionCore extends PartnerCore<ActionCore> {
|
||||
if (split.length < 2) {
|
||||
throw new MetaActionNotFoundException("未找到对应的行动程序,原因: 传入的 actionKey(" + actionKey + ") 存在异常");
|
||||
}
|
||||
MetaAction.Type type = BUILTIN_LOCATION.equals(split[0]) ? MetaAction.Type.BUILTIN : MetaAction.Type.MCP;
|
||||
return new MetaAction(
|
||||
split[1],
|
||||
metaActionInfo.isIo(),
|
||||
MetaAction.Type.MCP,
|
||||
type,
|
||||
split[0]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -14,12 +14,14 @@ data class MetaAction(
|
||||
*/
|
||||
val io: Boolean = false,
|
||||
/**
|
||||
* 行动程序类型,可分为 MCP、ORIGIN 两种,前者对应读取到的 MCP Tool、后者对应生成的临时行动程序
|
||||
* 行动程序类型,可分为 MCP、ORIGIN、BUILTIN 三种,
|
||||
* 分别对应读取到的 MCP Tool、生成的临时行动程序、本地内置行动
|
||||
*/
|
||||
val type: Type,
|
||||
/**
|
||||
* 当类型为 MCP 时,该字段对应相应 MCP Client 注册时生成的 id;
|
||||
* 当类型为 ORIGIN 时,该字段对应相应的磁盘路径字符串
|
||||
* 当类型为 ORIGIN 时,该字段对应相应的磁盘路径字符串;
|
||||
* 当类型为 BUILTIN 时,该字段固定为 builtin
|
||||
*/
|
||||
val location: String,
|
||||
) {
|
||||
@@ -67,7 +69,12 @@ data class MetaAction(
|
||||
/**
|
||||
* 适用于‘临时生成’的行动程序,在生成后根据序列化选项及执行情况,进行持久化
|
||||
*/
|
||||
ORIGIN
|
||||
ORIGIN,
|
||||
|
||||
/**
|
||||
* 由本地内置注册表直接执行的行动
|
||||
*/
|
||||
BUILTIN
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -224,6 +224,7 @@ public class LocalRunnerClient extends RunnerClient {
|
||||
response = switch (metaAction.getType()) {
|
||||
case MetaAction.Type.MCP -> doRunWithMcp(metaAction);
|
||||
case MetaAction.Type.ORIGIN -> doRunWithOrigin(metaAction);
|
||||
case MetaAction.Type.BUILTIN -> doRunWithBuiltin(metaAction);
|
||||
};
|
||||
} catch (Exception e) {
|
||||
response = new RunnerResponse();
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package work.slhaf.partner.core.action.runner;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import io.modelcontextprotocol.server.McpStatelessAsyncServer;
|
||||
import lombok.Data;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import lombok.val;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -11,6 +11,7 @@ import work.slhaf.partner.core.action.entity.MetaAction;
|
||||
import work.slhaf.partner.core.action.entity.MetaAction.Result;
|
||||
import work.slhaf.partner.core.action.entity.MetaActionInfo;
|
||||
import work.slhaf.partner.core.action.exception.ActionInitFailedException;
|
||||
import work.slhaf.partner.module.modules.action.builtin.BuiltinActionRegistry;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
@@ -45,8 +46,8 @@ public abstract class RunnerClient {
|
||||
|
||||
protected final ConcurrentHashMap<String, MetaActionInfo> existedMetaActions;
|
||||
protected final ExecutorService executor;
|
||||
//TODO 仍可提供内部 MCP,但调用方式需要结合 AgentContext来获取,否则生命周期不合
|
||||
protected McpStatelessAsyncServer innerMcpServer;
|
||||
@Setter
|
||||
protected BuiltinActionRegistry builtinActionRegistry;
|
||||
|
||||
/**
|
||||
* ActionCore 将注入虚拟线程池
|
||||
@@ -82,6 +83,23 @@ public abstract class RunnerClient {
|
||||
|
||||
public abstract void persistSerialize(MetaActionInfo metaActionInfo, ActionFileMetaData fileMetaData);
|
||||
|
||||
protected RunnerResponse doRunWithBuiltin(MetaAction metaAction) {
|
||||
RunnerResponse response = new RunnerResponse();
|
||||
if (builtinActionRegistry == null) {
|
||||
response.setOk(false);
|
||||
response.setData("BuiltinActionRegistry 未初始化");
|
||||
return response;
|
||||
}
|
||||
try {
|
||||
response.setData(builtinActionRegistry.call(metaAction.getKey(), metaAction.getParams()));
|
||||
response.setOk(true);
|
||||
} catch (Exception e) {
|
||||
response.setOk(false);
|
||||
response.setData(e.getLocalizedMessage());
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
protected void createPath(String pathStr) {
|
||||
val path = Path.of(pathStr);
|
||||
try {
|
||||
|
||||
@@ -30,8 +30,13 @@ public class SandboxRunnerClient extends RunnerClient {
|
||||
}
|
||||
|
||||
protected RunnerResponse doRun(MetaAction metaAction) {
|
||||
return switch (metaAction.getType()) {
|
||||
case BUILTIN -> doRunWithBuiltin(metaAction);
|
||||
case MCP, ORIGIN -> {
|
||||
// 调用沙盒执行器
|
||||
return null;
|
||||
yield null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
package work.slhaf.partner.module.modules.action.builtin;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import work.slhaf.partner.api.agent.factory.capability.annotation.InjectCapability;
|
||||
import work.slhaf.partner.api.agent.factory.component.abstracts.AbstractAgentModule;
|
||||
import work.slhaf.partner.api.agent.factory.component.annotation.Init;
|
||||
import work.slhaf.partner.core.action.ActionCapability;
|
||||
import work.slhaf.partner.core.action.entity.MetaActionInfo;
|
||||
import work.slhaf.partner.core.action.exception.MetaActionNotFoundException;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static work.slhaf.partner.core.action.ActionCore.BUILTIN_LOCATION;
|
||||
|
||||
public class BuiltinActionRegistry extends AbstractAgentModule.Standalone {
|
||||
|
||||
@Getter
|
||||
private final Map<String, BuiltinActionDefinition> definitions = new LinkedHashMap<>();
|
||||
@InjectCapability
|
||||
private ActionCapability actionCapability;
|
||||
|
||||
public static BuiltinActionDefinition definition(String name, MetaActionInfo metaActionInfo,
|
||||
Function<Map<String, Object>, Object> invoker) {
|
||||
return new BuiltinActionDefinition(BUILTIN_LOCATION + "::" + name, metaActionInfo, invoker);
|
||||
}
|
||||
|
||||
@Init
|
||||
public void init() {
|
||||
definitions.clear();
|
||||
for (BuiltinActionDefinition definition : buildDefinitions()) {
|
||||
definitions.put(definition.actionKey(), definition);
|
||||
}
|
||||
actionCapability.registerMetaActions(exportMetaActionInfos());
|
||||
actionCapability.runnerClient().setBuiltinActionRegistry(this);
|
||||
}
|
||||
|
||||
protected List<BuiltinActionDefinition> buildDefinitions() {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
public String call(@NonNull String actionKey, @NonNull Map<String, Object> params) {
|
||||
BuiltinActionDefinition definition = definitions.get(actionKey);
|
||||
if (definition == null) {
|
||||
throw new MetaActionNotFoundException("未找到对应的内置行动程序: " + actionKey);
|
||||
}
|
||||
Object result = definition.invoker().apply(params);
|
||||
if (result == null) {
|
||||
return "null";
|
||||
}
|
||||
if (result instanceof String string) {
|
||||
return string;
|
||||
}
|
||||
if (result instanceof Number || result instanceof Boolean || result instanceof Map || result instanceof Iterable) {
|
||||
return JSONObject.toJSONString(result);
|
||||
}
|
||||
return String.valueOf(result);
|
||||
}
|
||||
|
||||
private Map<String, MetaActionInfo> exportMetaActionInfos() {
|
||||
Map<String, MetaActionInfo> metaActions = new LinkedHashMap<>();
|
||||
definitions.forEach((key, value) -> metaActions.put(key, value.metaActionInfo()));
|
||||
return metaActions;
|
||||
}
|
||||
|
||||
public record BuiltinActionDefinition(
|
||||
String actionKey,
|
||||
MetaActionInfo metaActionInfo,
|
||||
Function<Map<String, Object>, Object> invoker
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import work.slhaf.partner.core.action.entity.MetaAction;
|
||||
import work.slhaf.partner.core.action.entity.MetaActionInfo;
|
||||
import work.slhaf.partner.module.modules.action.builtin.BuiltinActionRegistry;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
@@ -843,6 +844,53 @@ public class LocalRunnerClientTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDoRunWithBuiltin(@TempDir Path tempDir) {
|
||||
ConcurrentHashMap<String, MetaActionInfo> existedMetaActions = new ConcurrentHashMap<>();
|
||||
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
|
||||
LocalRunnerClient client = new LocalRunnerClient(existedMetaActions, executor, tempDir.toString());
|
||||
BuiltinActionRegistry registry = new BuiltinActionRegistry() {
|
||||
@Override
|
||||
protected List<BuiltinActionDefinition> buildDefinitions() {
|
||||
return List.of(
|
||||
definition("echo", buildMetaActionInfo("echo"), params -> params.get("value"))
|
||||
);
|
||||
}
|
||||
};
|
||||
client.setBuiltinActionRegistry(registry);
|
||||
registry.getDefinitions().put(
|
||||
"builtin::echo",
|
||||
BuiltinActionRegistry.definition("echo", buildMetaActionInfo("echo"), params -> params.get("value"))
|
||||
);
|
||||
|
||||
try {
|
||||
MetaAction metaAction = buildMetaAction(MetaAction.Type.BUILTIN, "builtin", "echo", Map.of("value", "ok"));
|
||||
RunnerClient.RunnerResponse response = client.doRun(metaAction);
|
||||
Assertions.assertNotNull(response);
|
||||
Assertions.assertTrue(response.isOk());
|
||||
Assertions.assertEquals("ok", response.getData());
|
||||
} finally {
|
||||
executor.shutdownNow();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDoRunWithBuiltinMissingRegistry(@TempDir Path tempDir) {
|
||||
ConcurrentHashMap<String, MetaActionInfo> existedMetaActions = new ConcurrentHashMap<>();
|
||||
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
|
||||
LocalRunnerClient client = new LocalRunnerClient(existedMetaActions, executor, tempDir.toString());
|
||||
|
||||
try {
|
||||
MetaAction metaAction = buildMetaAction(MetaAction.Type.BUILTIN, "builtin", "echo", Map.of());
|
||||
RunnerClient.RunnerResponse response = client.doRun(metaAction);
|
||||
Assertions.assertNotNull(response);
|
||||
Assertions.assertFalse(response.isOk());
|
||||
Assertions.assertEquals("BuiltinActionRegistry 未初始化", response.getData());
|
||||
} finally {
|
||||
executor.shutdownNow();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDoRunWithMcpLoadedFromCommonConfig(@TempDir Path tempDir) throws IOException, InterruptedException {
|
||||
ConcurrentHashMap<String, MetaActionInfo> existedMetaActions = new ConcurrentHashMap<>();
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
package work.slhaf.partner.module.modules.action.builtin;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import work.slhaf.partner.core.action.ActionCapability;
|
||||
import work.slhaf.partner.core.action.ActionCore;
|
||||
import work.slhaf.partner.core.action.entity.MetaActionInfo;
|
||||
import work.slhaf.partner.core.action.exception.MetaActionNotFoundException;
|
||||
import work.slhaf.partner.core.action.runner.RunnerClient;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
class BuiltinActionRegistryTest {
|
||||
|
||||
private static void injectActionCapability(BuiltinActionRegistry registry, ActionCapability actionCapability) throws Exception {
|
||||
Field field = BuiltinActionRegistry.class.getDeclaredField("actionCapability");
|
||||
field.setAccessible(true);
|
||||
field.set(registry, actionCapability);
|
||||
}
|
||||
|
||||
private static Map<String, BuiltinActionRegistry.BuiltinActionDefinition> indexDefinitions(
|
||||
List<BuiltinActionRegistry.BuiltinActionDefinition> definitions
|
||||
) {
|
||||
Map<String, BuiltinActionRegistry.BuiltinActionDefinition> map = new HashMap<>();
|
||||
for (BuiltinActionRegistry.BuiltinActionDefinition definition : definitions) {
|
||||
map.put(definition.actionKey(), definition);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private static MetaActionInfo buildMetaActionInfo(String description) {
|
||||
MetaActionInfo info = new MetaActionInfo();
|
||||
info.setDescription(description);
|
||||
info.setParams(new HashMap<>());
|
||||
return info;
|
||||
}
|
||||
|
||||
@Test
|
||||
void testInitRegistersMetaActionsAndMountsRunner() throws Exception {
|
||||
ActionCapability actionCapability = mock(ActionCapability.class);
|
||||
RunnerClient runnerClient = mock(RunnerClient.class);
|
||||
when(actionCapability.runnerClient()).thenReturn(runnerClient);
|
||||
|
||||
BuiltinActionRegistry registry = new TestRegistry(List.of(
|
||||
BuiltinActionRegistry.definition("echo", buildMetaActionInfo("echo"), params -> params.get("value"))
|
||||
));
|
||||
injectActionCapability(registry, actionCapability);
|
||||
|
||||
registry.init();
|
||||
|
||||
verify(actionCapability).registerMetaActions(argThat(metaActions ->
|
||||
metaActions.containsKey("builtin::echo")
|
||||
&& "echo".equals(metaActions.get("builtin::echo").getDescription())
|
||||
));
|
||||
verify(runnerClient).setBuiltinActionRegistry(registry);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCallReturnsStringifiedResults() {
|
||||
BuiltinActionRegistry registry = new TestRegistry(List.of(
|
||||
BuiltinActionRegistry.definition("echo", buildMetaActionInfo("echo"), params -> params.get("value")),
|
||||
BuiltinActionRegistry.definition("json", buildMetaActionInfo("json"), params -> Map.of("ok", true)),
|
||||
BuiltinActionRegistry.definition("nil", buildMetaActionInfo("nil"), params -> null)
|
||||
));
|
||||
|
||||
registry.getDefinitions().putAll(indexDefinitions(registry.buildDefinitions()));
|
||||
|
||||
Assertions.assertEquals("hello", registry.call("builtin::echo", Map.of("value", "hello")));
|
||||
Assertions.assertEquals("{\"ok\":true}", registry.call("builtin::json", Map.of()));
|
||||
Assertions.assertEquals("null", registry.call("builtin::nil", Map.of()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCallThrowsWhenMissingDefinition() {
|
||||
BuiltinActionRegistry registry = new TestRegistry(List.of());
|
||||
Assertions.assertThrows(MetaActionNotFoundException.class, () -> registry.call("builtin::missing", Map.of()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCallPropagatesInvokerFailure() {
|
||||
BuiltinActionRegistry registry = new TestRegistry(List.of(
|
||||
BuiltinActionRegistry.definition("boom", buildMetaActionInfo("boom"), params -> {
|
||||
throw new IllegalStateException("boom");
|
||||
})
|
||||
));
|
||||
registry.getDefinitions().putAll(indexDefinitions(registry.buildDefinitions()));
|
||||
|
||||
IllegalStateException exception = Assertions.assertThrows(IllegalStateException.class,
|
||||
() -> registry.call("builtin::boom", Map.of()));
|
||||
Assertions.assertEquals("boom", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testActionCoreLoadsBuiltinMetaAction() throws Exception {
|
||||
ActionCore actionCore = new ActionCore();
|
||||
try {
|
||||
actionCore.registerMetaActions(Map.of("builtin::echo", buildMetaActionInfo("echo")));
|
||||
|
||||
Assertions.assertTrue(actionCore.listAvailableMetaActions().containsKey("builtin::echo"));
|
||||
Assertions.assertEquals("echo", actionCore.loadMetaActionInfo("builtin::echo").getDescription());
|
||||
Assertions.assertEquals("builtin::echo", actionCore.loadMetaAction("builtin::echo").getKey());
|
||||
Assertions.assertEquals(ActionCore.BUILTIN_LOCATION, actionCore.loadMetaAction("builtin::echo").getLocation());
|
||||
} finally {
|
||||
actionCore.getExecutor(ActionCore.ExecutorType.PLATFORM).shutdownNow();
|
||||
actionCore.getExecutor(ActionCore.ExecutorType.VIRTUAL).shutdownNow();
|
||||
}
|
||||
}
|
||||
|
||||
private static class TestRegistry extends BuiltinActionRegistry {
|
||||
private final List<BuiltinActionDefinition> definitions;
|
||||
|
||||
private TestRegistry(List<BuiltinActionDefinition> definitions) {
|
||||
this.definitions = definitions;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<BuiltinActionDefinition> buildDefinitions() {
|
||||
return definitions;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user