refactor(action): switch builtin actions to provider-driven self-registration

This commit is contained in:
2026-04-27 20:53:45 +08:00
parent cf61c171a5
commit 3eac52f4e2
7 changed files with 83 additions and 33 deletions

View File

@@ -2,7 +2,7 @@ package work.slhaf.partner.module.action.builtin;
import java.util.List; import java.util.List;
interface BuiltinActionProvider { public interface BuiltinActionProvider {
List<BuiltinActionRegistry.BuiltinActionDefinition> provideBuiltinActions(); List<BuiltinActionRegistry.BuiltinActionDefinition> provideBuiltinActions();
String createActionKey(String actionName); String createActionKey(String actionName);

View File

@@ -9,7 +9,6 @@ import work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCa
import work.slhaf.partner.framework.agent.factory.component.abstracts.AbstractAgentModule; import work.slhaf.partner.framework.agent.factory.component.abstracts.AbstractAgentModule;
import work.slhaf.partner.framework.agent.factory.component.annotation.Init; import work.slhaf.partner.framework.agent.factory.component.annotation.Init;
import java.util.ArrayList;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -26,32 +25,26 @@ public class BuiltinActionRegistry extends AbstractAgentModule.Standalone {
@Init @Init
public void init() { public void init() {
definitions.clear();
for (BuiltinActionDefinition definition : buildDefaultActionDefinitions()) {
definitions.put(definition.actionKey(), definition);
}
actionCapability.registerMetaActions(exportMetaActionInfos());
actionCapability.runnerClient().setBuiltinActionRegistry(this); actionCapability.runnerClient().setBuiltinActionRegistry(this);
} }
protected List<BuiltinActionDefinition> buildDefaultActionDefinitions() { public void register(BuiltinActionProvider provider) {
List<BuiltinActionDefinition> builtinActionDefinitions = new ArrayList<>(); List<BuiltinActionDefinition> builtinActionDefinitions = provider.provideBuiltinActions();
BuiltinActionProvider commandActionProvider = new BuiltinCommandActionProvider(); if (builtinActionDefinitions == null || builtinActionDefinitions.isEmpty()) {
BuiltinActionProvider capabilityActionProvider = new BuiltinCapabilityActionProvider(); return;
BuiltinActionProvider interventionActionProvider = new BuiltinInterventionActionProvider(); }
BuiltinActionProvider dynamicActionProvider = new BuiltinDynamicActionProvider(); Map<String, MetaActionInfo> metaActionInfos = new LinkedHashMap<>();
for (BuiltinActionDefinition definition : builtinActionDefinitions) {
builtinActionDefinitions.addAll(commandActionProvider.provideBuiltinActions()); definitions.put(definition.actionKey(), definition);
builtinActionDefinitions.addAll(capabilityActionProvider.provideBuiltinActions()); metaActionInfos.put(definition.actionKey(), definition.metaActionInfo());
builtinActionDefinitions.addAll(interventionActionProvider.provideBuiltinActions()); }
builtinActionDefinitions.addAll(dynamicActionProvider.provideBuiltinActions()); actionCapability.registerMetaActions(metaActionInfos);
return builtinActionDefinitions;
} }
public void defineBuiltinAction(String name, MetaActionInfo metaActionInfo, Function<Map<String, Object>, String> invoker) { public void defineBuiltinAction(String name, MetaActionInfo metaActionInfo, Function<Map<String, Object>, String> invoker) {
BuiltinActionDefinition definition = new BuiltinActionDefinition(BUILTIN_LOCATION + "::" + name, metaActionInfo, invoker); BuiltinActionDefinition definition = new BuiltinActionDefinition(BUILTIN_LOCATION + "::" + name, metaActionInfo, invoker);
definitions.put(definition.actionKey(), definition); definitions.put(definition.actionKey(), definition);
actionCapability.registerMetaActions(Map.of(definition.actionKey(), definition.metaActionInfo()));
} }
public String call(@NonNull String actionKey, @NonNull Map<String, Object> params) { public String call(@NonNull String actionKey, @NonNull Map<String, Object> params) {
@@ -70,11 +63,6 @@ public class BuiltinActionRegistry extends AbstractAgentModule.Standalone {
return result; return 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( public record BuiltinActionDefinition(
String actionKey, String actionKey,

View File

@@ -14,6 +14,8 @@ import work.slhaf.partner.core.memory.pojo.MemorySlice;
import work.slhaf.partner.core.memory.pojo.MemoryUnit; import work.slhaf.partner.core.memory.pojo.MemoryUnit;
import work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCapability; import work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCapability;
import work.slhaf.partner.framework.agent.factory.component.annotation.AgentComponent; import work.slhaf.partner.framework.agent.factory.component.annotation.AgentComponent;
import work.slhaf.partner.framework.agent.factory.component.annotation.Init;
import work.slhaf.partner.framework.agent.factory.component.annotation.InjectModule;
import work.slhaf.partner.framework.agent.support.Result; import work.slhaf.partner.framework.agent.support.Result;
import java.util.*; import java.util.*;
@@ -32,6 +34,13 @@ class BuiltinCapabilityActionProvider implements BuiltinActionProvider {
private CognitionCapability cognitionCapability; private CognitionCapability cognitionCapability;
@InjectCapability @InjectCapability
private MemoryCapability memoryCapability; private MemoryCapability memoryCapability;
@InjectModule
private BuiltinActionRegistry builtinActionRegistry;
@Init
public void init() {
builtinActionRegistry.register(this);
}
@Override @Override
public List<BuiltinActionRegistry.BuiltinActionDefinition> provideBuiltinActions() { public List<BuiltinActionRegistry.BuiltinActionDefinition> provideBuiltinActions() {

View File

@@ -6,6 +6,9 @@ import work.slhaf.partner.core.action.entity.MetaActionInfo;
import work.slhaf.partner.core.action.runner.execution.CommandExecutionService; import work.slhaf.partner.core.action.runner.execution.CommandExecutionService;
import work.slhaf.partner.core.action.runner.policy.ExecutionPolicyRegistry; import work.slhaf.partner.core.action.runner.policy.ExecutionPolicyRegistry;
import work.slhaf.partner.core.action.runner.policy.WrappedLaunchSpec; import work.slhaf.partner.core.action.runner.policy.WrappedLaunchSpec;
import work.slhaf.partner.framework.agent.factory.component.annotation.AgentComponent;
import work.slhaf.partner.framework.agent.factory.component.annotation.Init;
import work.slhaf.partner.framework.agent.factory.component.annotation.InjectModule;
import java.io.IOException; import java.io.IOException;
import java.io.RandomAccessFile; import java.io.RandomAccessFile;
@@ -21,6 +24,7 @@ import java.util.function.Function;
import static work.slhaf.partner.core.action.ActionCore.BUILTIN_LOCATION; import static work.slhaf.partner.core.action.ActionCore.BUILTIN_LOCATION;
@AgentComponent
class BuiltinCommandActionProvider implements BuiltinActionProvider { class BuiltinCommandActionProvider implements BuiltinActionProvider {
private static final String COMMAND_LOCATION = BUILTIN_LOCATION + "::" + "command"; private static final String COMMAND_LOCATION = BUILTIN_LOCATION + "::" + "command";
@@ -38,6 +42,14 @@ class BuiltinCommandActionProvider implements BuiltinActionProvider {
private final ConcurrentHashMap<String, CommandHandle> commandHandles = new ConcurrentHashMap<>(); private final ConcurrentHashMap<String, CommandHandle> commandHandles = new ConcurrentHashMap<>();
private final CommandExecutionService commandExecutionService = CommandExecutionService.INSTANCE; private final CommandExecutionService commandExecutionService = CommandExecutionService.INSTANCE;
@InjectModule
private BuiltinActionRegistry builtinActionRegistry;
@Init
public void init() {
builtinActionRegistry.register(this);
}
private Map<String, String> commandParams() { private Map<String, String> commandParams() {
Map<String, String> params = new LinkedHashMap<>(); Map<String, String> params = new LinkedHashMap<>();
params.put("arg", param("required", "string", "Command executable name or path.")); params.put("arg", param("required", "string", "Command executable name or path."));

View File

@@ -7,6 +7,7 @@ import work.slhaf.partner.core.action.ActionCapability;
import work.slhaf.partner.core.action.entity.*; import work.slhaf.partner.core.action.entity.*;
import work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCapability; import work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCapability;
import work.slhaf.partner.framework.agent.factory.component.annotation.AgentComponent; import work.slhaf.partner.framework.agent.factory.component.annotation.AgentComponent;
import work.slhaf.partner.framework.agent.factory.component.annotation.Init;
import work.slhaf.partner.framework.agent.factory.component.annotation.InjectModule; import work.slhaf.partner.framework.agent.factory.component.annotation.InjectModule;
import work.slhaf.partner.module.action.scheduler.ActionScheduler; import work.slhaf.partner.module.action.scheduler.ActionScheduler;
@@ -29,6 +30,13 @@ class BuiltinDynamicActionProvider implements BuiltinActionProvider {
private ActionCapability actionCapability; private ActionCapability actionCapability;
@InjectModule @InjectModule
private ActionScheduler actionScheduler; private ActionScheduler actionScheduler;
@InjectModule
private BuiltinActionRegistry builtinActionRegistry;
@Init
public void init() {
builtinActionRegistry.register(this);
}
@Override @Override
public List<BuiltinActionRegistry.BuiltinActionDefinition> provideBuiltinActions() { public List<BuiltinActionRegistry.BuiltinActionDefinition> provideBuiltinActions() {

View File

@@ -17,6 +17,8 @@ import work.slhaf.partner.framework.agent.exception.AgentRuntimeException;
import work.slhaf.partner.framework.agent.exception.ExceptionReporterHandler; import work.slhaf.partner.framework.agent.exception.ExceptionReporterHandler;
import work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCapability; import work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCapability;
import work.slhaf.partner.framework.agent.factory.component.annotation.AgentComponent; import work.slhaf.partner.framework.agent.factory.component.annotation.AgentComponent;
import work.slhaf.partner.framework.agent.factory.component.annotation.Init;
import work.slhaf.partner.framework.agent.factory.component.annotation.InjectModule;
import work.slhaf.partner.framework.agent.support.Result; import work.slhaf.partner.framework.agent.support.Result;
import java.util.*; import java.util.*;
@@ -35,6 +37,13 @@ class BuiltinInterventionActionProvider implements BuiltinActionProvider {
private ActionCapability actionCapability; private ActionCapability actionCapability;
@InjectCapability @InjectCapability
private CognitionCapability cognitionCapability; private CognitionCapability cognitionCapability;
@InjectModule
private BuiltinActionRegistry builtinActionRegistry;
@Init
public void init() {
builtinActionRegistry.register(this);
}
@Override @Override
public List<BuiltinActionRegistry.BuiltinActionDefinition> provideBuiltinActions() { public List<BuiltinActionRegistry.BuiltinActionDefinition> provideBuiltinActions() {

View File

@@ -52,29 +52,51 @@ class BuiltinActionRegistryTest {
} }
@Test @Test
void testInitRegistersMetaActionsAndMountsRunner() throws Exception { void testInitMountsRunner() throws Exception {
ActionCapability actionCapability = mock(ActionCapability.class); ActionCapability actionCapability = mock(ActionCapability.class);
RunnerClient runnerClient = mock(RunnerClient.class); RunnerClient runnerClient = mock(RunnerClient.class);
when(actionCapability.runnerClient()).thenReturn(runnerClient); when(actionCapability.runnerClient()).thenReturn(runnerClient);
BuiltinActionRegistry registry = spy(new BuiltinActionRegistry()); BuiltinActionRegistry registry = new BuiltinActionRegistry();
doReturn(List.of(
buildDefinition("echo", buildMetaActionInfo("echo"), params -> params.get("value").toString())
)).when(registry).buildDefaultActionDefinitions();
injectActionCapability(registry, actionCapability); injectActionCapability(registry, actionCapability);
registry.init(); registry.init();
verify(runnerClient).setBuiltinActionRegistry(registry);
}
@Test
void testRegisterProviderRegistersDefinitionsAndMetaActions() throws Exception {
ActionCapability actionCapability = mock(ActionCapability.class);
BuiltinActionRegistry registry = new BuiltinActionRegistry();
injectActionCapability(registry, actionCapability);
BuiltinActionProvider provider = new BuiltinActionProvider() {
@Override
public List<BuiltinActionRegistry.BuiltinActionDefinition> provideBuiltinActions() {
return List.of(buildDefinition("echo", buildMetaActionInfo("echo"), params -> params.get("value").toString()));
}
@Override
public String createActionKey(String actionName) {
return BUILTIN_LOCATION + "::" + actionName;
}
};
registry.register(provider);
verify(actionCapability).registerMetaActions(argThat(metaActions -> verify(actionCapability).registerMetaActions(argThat(metaActions ->
metaActions.containsKey("builtin::echo") metaActions.containsKey("builtin::echo")
&& "echo".equals(metaActions.get("builtin::echo").getDescription()) && "echo".equals(metaActions.get("builtin::echo").getDescription())
)); ));
verify(runnerClient).setBuiltinActionRegistry(registry); Assertions.assertEquals("hello", registry.call("builtin::echo", Map.of("value", "hello")));
} }
@Test @Test
void testCallReturnsStringifiedResults() { void testCallReturnsStringifiedResults() throws Exception {
ActionCapability actionCapability = mock(ActionCapability.class);
BuiltinActionRegistry registry = new BuiltinActionRegistry(); BuiltinActionRegistry registry = new BuiltinActionRegistry();
injectActionCapability(registry, actionCapability);
registry.defineBuiltinAction("echo", buildMetaActionInfo("echo"), params -> params.get("value").toString()); registry.defineBuiltinAction("echo", buildMetaActionInfo("echo"), params -> params.get("value").toString());
registry.defineBuiltinAction("json", buildMetaActionInfo("json"), params -> Map.of("ok", true).toString()); registry.defineBuiltinAction("json", buildMetaActionInfo("json"), params -> Map.of("ok", true).toString());
registry.defineBuiltinAction("nil", buildMetaActionInfo("nil"), params -> null); registry.defineBuiltinAction("nil", buildMetaActionInfo("nil"), params -> null);
@@ -91,8 +113,10 @@ class BuiltinActionRegistryTest {
} }
@Test @Test
void testCallPropagatesInvokerFailure() { void testCallPropagatesInvokerFailure() throws Exception {
ActionCapability actionCapability = mock(ActionCapability.class);
BuiltinActionRegistry registry = new BuiltinActionRegistry(); BuiltinActionRegistry registry = new BuiltinActionRegistry();
injectActionCapability(registry, actionCapability);
registry.defineBuiltinAction("boom", buildMetaActionInfo("boom"), params -> { registry.defineBuiltinAction("boom", buildMetaActionInfo("boom"), params -> {
throw new IllegalStateException("boom"); throw new IllegalStateException("boom");
}); });