fix(action): correct action chain fixing error

This commit is contained in:
2026-04-20 22:26:31 +08:00
parent d90c514159
commit d1da99c44d
4 changed files with 135 additions and 4 deletions

View File

@@ -399,6 +399,9 @@ public class ActionPlanner extends AbstractAgentModule.Running<PartnerRunningFlo
} }
private boolean fixDependencies(Map<Integer, List<String>> primaryActionChain) { private boolean fixDependencies(Map<Integer, List<String>> primaryActionChain) {
if (primaryActionChain == null || primaryActionChain.isEmpty()) {
return false;
}
// 先将 primaryActionChain 的节点序号修正为从1开始依次增大 // 先将 primaryActionChain 的节点序号修正为从1开始依次增大
fixOrder(primaryActionChain); fixOrder(primaryActionChain);
List<Integer> fixedOrders = new ArrayList<>(primaryActionChain.keySet().stream().toList()); List<Integer> fixedOrders = new ArrayList<>(primaryActionChain.keySet().stream().toList());
@@ -409,6 +412,9 @@ public class ActionPlanner extends AbstractAgentModule.Running<PartnerRunningFlo
for (Integer fixedOrder : fixedOrders) { for (Integer fixedOrder : fixedOrders) {
int lastOrder = fixedOrder - 1; int lastOrder = fixedOrder - 1;
List<String> actionKeys = primaryActionChain.get(fixedOrder); List<String> actionKeys = primaryActionChain.get(fixedOrder);
if (actionKeys == null || actionKeys.isEmpty()) {
continue;
}
for (String actionKey : actionKeys) { for (String actionKey : actionKeys) {
// 根据 actionKey 加载行动信息,并检查是否存在必需前置依赖 // 根据 actionKey 加载行动信息,并检查是否存在必需前置依赖
@@ -451,9 +457,12 @@ public class ActionPlanner extends AbstractAgentModule.Running<PartnerRunningFlo
private void fixOrder(Map<Integer, List<String>> primaryActionChain) { private void fixOrder(Map<Integer, List<String>> primaryActionChain) {
Map<Integer, List<String>> tempChain = new HashMap<>(primaryActionChain); Map<Integer, List<String>> tempChain = new HashMap<>(primaryActionChain);
primaryActionChain.clear(); primaryActionChain.clear();
int chainSize = tempChain.size(); List<Integer> orders = new ArrayList<>(tempChain.keySet());
for (int i = 0; i < chainSize; i++) { orders.sort(Integer::compareTo);
primaryActionChain.put(i, tempChain.get(i)); int fixedOrder = 1;
for (Integer order : orders) {
List<String> actionKeys = tempChain.get(order);
primaryActionChain.put(fixedOrder++, actionKeys == null ? new ArrayList<>() : new ArrayList<>(actionKeys));
} }
} }

View File

@@ -3,6 +3,7 @@ package work.slhaf.partner.module.action.planner.evaluator.entity;
import lombok.Data; import lombok.Data;
import work.slhaf.partner.core.action.entity.Schedulable; import work.slhaf.partner.core.action.entity.Schedulable;
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;
@@ -31,9 +32,15 @@ public class EvaluatorResult {
} }
public Map<Integer, List<String>> getPrimaryActionChainAsMap() { public Map<Integer, List<String>> getPrimaryActionChainAsMap() {
if (primaryActionChain == null || primaryActionChain.isEmpty()) {
return new LinkedHashMap<>();
}
return primaryActionChain.stream().collect(Collectors.toMap( return primaryActionChain.stream().collect(Collectors.toMap(
ChainElement::getOrder, ChainElement::getOrder,
ChainElement::getActionKeys, chainElement -> {
List<String> actionKeys = chainElement.getActionKeys();
return actionKeys == null ? new ArrayList<>() : new ArrayList<>(actionKeys);
},
(oldValue, newValue) -> newValue, (oldValue, newValue) -> newValue,
LinkedHashMap::new LinkedHashMap::new
)); ));

View File

@@ -0,0 +1,65 @@
package work.slhaf.partner.module.action.planner;
import com.alibaba.fastjson2.JSONObject;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import work.slhaf.partner.core.action.ActionCapability;
import work.slhaf.partner.core.action.entity.MetaActionInfo;
import work.slhaf.partner.framework.agent.support.Result;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import static org.junit.jupiter.api.Assertions.*;
class ActionPlannerAssemblyHelperTest {
private static void injectField(Object target, String fieldName, Object value) throws Exception {
Field field = target.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(target, value);
}
private static Object getField(Object target, String fieldName) throws Exception {
Field field = target.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
return field.get(target);
}
@Test
void shouldFixNonZeroBasedOrdersWithoutInjectingNullActionKeyList() throws Exception {
ActionCapability actionCapability = Mockito.mock(ActionCapability.class);
Mockito.when(actionCapability.loadMetaActionInfo(Mockito.anyString()))
.thenReturn(Result.success(new MetaActionInfo(
false,
null,
Map.of(),
"desc",
Set.of(),
Set.of(),
Set.of(),
false,
new JSONObject()
)));
ActionPlanner planner = new ActionPlanner();
injectField(planner, "actionCapability", actionCapability);
Object helper = getField(planner, "assemblyHelper");
Method fixDependencies = helper.getClass().getDeclaredMethod("fixDependencies", Map.class);
fixDependencies.setAccessible(true);
Map<Integer, List<String>> chain = new LinkedHashMap<>();
chain.put(1, new ArrayList<>(List.of("action_a")));
chain.put(2, new ArrayList<>(List.of("action_b")));
boolean fixed = (boolean) fixDependencies.invoke(helper, chain);
assertTrue(fixed);
assertEquals(List.of(1, 2), new ArrayList<>(chain.keySet()));
assertEquals(List.of("action_a"), chain.get(1));
assertEquals(List.of("action_b"), chain.get(2));
assertFalse(chain.containsValue(null));
}
}

View File

@@ -0,0 +1,50 @@
package work.slhaf.partner.module.action.planner.evaluator.entity;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.*;
class EvaluatorResultTest {
@Test
void shouldReturnEmptyMapWhenPrimaryActionChainIsNull() {
EvaluatorResult result = new EvaluatorResult();
Map<Integer, List<String>> chain = result.getPrimaryActionChainAsMap();
assertNotNull(chain);
assertTrue(chain.isEmpty());
}
@Test
void shouldNormalizeNullActionKeysToEmptyList() {
EvaluatorResult result = new EvaluatorResult();
EvaluatorResult.ChainElement element = new EvaluatorResult.ChainElement();
element.setOrder(1);
element.setActionKeys(null);
result.setPrimaryActionChain(List.of(element));
Map<Integer, List<String>> chain = result.getPrimaryActionChainAsMap();
assertEquals(1, chain.size());
assertNotNull(chain.get(1));
assertTrue(chain.get(1).isEmpty());
}
@Test
void shouldCopyActionKeyListDefensively() {
EvaluatorResult result = new EvaluatorResult();
EvaluatorResult.ChainElement element = new EvaluatorResult.ChainElement();
element.setOrder(1);
List<String> keys = new ArrayList<>(List.of("a"));
element.setActionKeys(keys);
result.setPrimaryActionChain(List.of(element));
Map<Integer, List<String>> chain = result.getPrimaryActionChainAsMap();
keys.add("b");
assertEquals(new LinkedHashMap<>(Map.of(1, List.of("a"))), new LinkedHashMap<>(chain));
}
}