From 93304878ad975fe33999ad4f8ada029bd94535dc Mon Sep 17 00:00:00 2001 From: slhafzjw Date: Sun, 12 Apr 2026 20:13:11 +0800 Subject: [PATCH] refactor(memory): migrate slice/topic/date lookup to Result flow and unify MemoryLookupException reporting --- .../partner/core/memory/MemoryCapability.java | 3 +- .../slhaf/partner/core/memory/MemoryCore.java | 18 ++++++-- .../BuiltinCapabilityActionProvider.java | 11 ++--- .../module/memory/runtime/MemoryRuntime.java | 30 ++++++++++---- .../exception/MemoryLookupException.java | 30 ++++++++++++++ .../UnExistedDateIndexException.java | 7 ---- .../exception/UnExistedTopicException.java | 7 ---- .../memory/selector/MemorySelector.java | 8 ++-- .../extractor/MemorySelectExtractor.java | 10 ++--- .../extractor/entity/ExtractorResult.java | 1 - .../module/memory/updater/MemoryUpdater.java | 38 ++++++++--------- .../updater/summarizer/MultiSummarizer.java | 20 ++++----- .../updater/summarizer/SingleSummarizer.java | 14 +++---- .../partner/core/memory/MemoryCoreTest.java | 2 +- .../memory/runtime/MemoryRuntimeTest.java | 41 ++++++++++++++++--- .../memory/updater/MemoryUpdaterTest.java | 25 +++++++---- 16 files changed, 166 insertions(+), 99 deletions(-) create mode 100644 Partner-Core/src/main/java/work/slhaf/partner/module/memory/runtime/exception/MemoryLookupException.java delete mode 100644 Partner-Core/src/main/java/work/slhaf/partner/module/memory/runtime/exception/UnExistedDateIndexException.java delete mode 100644 Partner-Core/src/main/java/work/slhaf/partner/module/memory/runtime/exception/UnExistedTopicException.java diff --git a/Partner-Core/src/main/java/work/slhaf/partner/core/memory/MemoryCapability.java b/Partner-Core/src/main/java/work/slhaf/partner/core/memory/MemoryCapability.java index d5dbd493..95d4605c 100644 --- a/Partner-Core/src/main/java/work/slhaf/partner/core/memory/MemoryCapability.java +++ b/Partner-Core/src/main/java/work/slhaf/partner/core/memory/MemoryCapability.java @@ -4,6 +4,7 @@ import work.slhaf.partner.core.memory.pojo.MemorySlice; import work.slhaf.partner.core.memory.pojo.MemoryUnit; import work.slhaf.partner.framework.agent.factory.capability.annotation.Capability; import work.slhaf.partner.framework.agent.model.pojo.Message; +import work.slhaf.partner.framework.agent.support.Result; import java.util.Collection; import java.util.List; @@ -13,7 +14,7 @@ public interface MemoryCapability { MemoryUnit getMemoryUnit(String unitId); - MemorySlice getMemorySlice(String unitId, String sliceId); + Result getMemorySlice(String unitId, String sliceId); MemoryUnit updateMemoryUnit(List chatMessages, String summary); diff --git a/Partner-Core/src/main/java/work/slhaf/partner/core/memory/MemoryCore.java b/Partner-Core/src/main/java/work/slhaf/partner/core/memory/MemoryCore.java index ddb71263..d74c84aa 100644 --- a/Partner-Core/src/main/java/work/slhaf/partner/core/memory/MemoryCore.java +++ b/Partner-Core/src/main/java/work/slhaf/partner/core/memory/MemoryCore.java @@ -12,6 +12,8 @@ import work.slhaf.partner.framework.agent.model.pojo.Message; import work.slhaf.partner.framework.agent.state.State; import work.slhaf.partner.framework.agent.state.StateSerializable; import work.slhaf.partner.framework.agent.state.StateValue; +import work.slhaf.partner.framework.agent.support.Result; +import work.slhaf.partner.module.memory.runtime.exception.MemoryLookupException; import java.nio.file.Path; import java.util.*; @@ -71,17 +73,25 @@ public class MemoryCore implements StateSerializable { } @CapabilityMethod - public MemorySlice getMemorySlice(String unitId, String sliceId) { + public Result getMemorySlice(String unitId, String sliceId) { MemoryUnit memoryUnit = memoryUnits.get(unitId); if (memoryUnit == null || memoryUnit.getSlices() == null) { - return null; + return Result.failure(new MemoryLookupException( + "Memory slice not found: " + unitId + ":" + sliceId, + unitId + ":" + sliceId, + "MEMORY_SLICE" + )); } for (MemorySlice slice : memoryUnit.getSlices()) { if (sliceId.equals(slice.getId())) { - return slice; + return Result.success(slice); } } - return null; + return Result.failure(new MemoryLookupException( + "Memory slice not found: " + unitId + ":" + sliceId, + unitId + ":" + sliceId, + "MEMORY_SLICE" + )); } @CapabilityMethod diff --git a/Partner-Core/src/main/java/work/slhaf/partner/module/action/builtin/BuiltinCapabilityActionProvider.java b/Partner-Core/src/main/java/work/slhaf/partner/module/action/builtin/BuiltinCapabilityActionProvider.java index 18e3abf0..87eaf08d 100644 --- a/Partner-Core/src/main/java/work/slhaf/partner/module/action/builtin/BuiltinCapabilityActionProvider.java +++ b/Partner-Core/src/main/java/work/slhaf/partner/module/action/builtin/BuiltinCapabilityActionProvider.java @@ -14,6 +14,7 @@ import work.slhaf.partner.core.memory.pojo.MemorySlice; 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.component.annotation.AgentComponent; +import work.slhaf.partner.framework.agent.support.Result; import java.util.*; import java.util.function.Function; @@ -66,14 +67,14 @@ class BuiltinCapabilityActionProvider implements BuiltinActionProvider { Function, String> invoker = params -> { String unitId = BuiltinActionRegistry.BuiltinActionDefinition.requireString(params, "unit_id"); String sliceId = BuiltinActionRegistry.BuiltinActionDefinition.requireString(params, "slice_id"); - MemorySlice slice = memoryCapability.getMemorySlice(unitId, sliceId); - - if (slice == null) { + Result sliceResult = memoryCapability.getMemorySlice(unitId, sliceId); + if (sliceResult.exceptionOrNull() != null) { return JSONObject.of( "ok", false, - "message", "Memory slice not found" + "message", sliceResult.exceptionOrNull().getLocalizedMessage() ).toJSONString(); } + MemorySlice slice = sliceResult.getOrThrow(); MemoryUnit unit = memoryCapability.getMemoryUnit(unitId); cognitionCapability.contextWorkspace().register(new ContextBlock( @@ -85,7 +86,7 @@ class BuiltinCapabilityActionProvider implements BuiltinActionProvider { )); return JSONObject.of( - "ok", false, + "ok", true, "message", "Memory slice found and recalled into context" ).toJSONString(); }; diff --git a/Partner-Core/src/main/java/work/slhaf/partner/module/memory/runtime/MemoryRuntime.java b/Partner-Core/src/main/java/work/slhaf/partner/module/memory/runtime/MemoryRuntime.java index d7274f2b..fc2890c2 100644 --- a/Partner-Core/src/main/java/work/slhaf/partner/module/memory/runtime/MemoryRuntime.java +++ b/Partner-Core/src/main/java/work/slhaf/partner/module/memory/runtime/MemoryRuntime.java @@ -10,6 +10,7 @@ import work.slhaf.partner.core.memory.MemoryCapability; import work.slhaf.partner.core.memory.pojo.MemorySlice; import work.slhaf.partner.core.memory.pojo.MemoryUnit; import work.slhaf.partner.core.memory.pojo.SliceRef; +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.component.abstracts.AbstractAgentModule; import work.slhaf.partner.framework.agent.factory.component.annotation.Init; @@ -17,8 +18,8 @@ import work.slhaf.partner.framework.agent.model.pojo.Message; import work.slhaf.partner.framework.agent.state.State; import work.slhaf.partner.framework.agent.state.StateSerializable; import work.slhaf.partner.framework.agent.state.StateValue; -import work.slhaf.partner.module.memory.runtime.exception.UnExistedDateIndexException; -import work.slhaf.partner.module.memory.runtime.exception.UnExistedTopicException; +import work.slhaf.partner.framework.agent.support.Result; +import work.slhaf.partner.module.memory.runtime.exception.MemoryLookupException; import work.slhaf.partner.module.memory.selector.ActivatedMemorySlice; import java.nio.file.Path; @@ -105,16 +106,26 @@ public class MemoryRuntime extends AbstractAgentModule.Standalone implements Sta private List findByTopicPath(String topicPath) { String normalizedPath = normalizeTopicPath(topicPath); List refs = topicSlices.get(normalizedPath); - if (refs == null || refs.isEmpty()) { - throw new UnExistedTopicException("不存在的主题: " + normalizedPath); + if (refs == null) { + ExceptionReporterHandler.INSTANCE.report(new MemoryLookupException( + "Unexisted topic path: " + normalizedPath, + normalizedPath, + "TOPIC" + )); + return List.of(); } return new ArrayList<>(refs); } private List findByDate(LocalDate date) { List refs = dateIndex.get(date); - if (refs == null || refs.isEmpty()) { - throw new UnExistedDateIndexException("不存在的日期索引: " + date); + if (refs == null) { + ExceptionReporterHandler.INSTANCE.report(new MemoryLookupException( + "Unexisted date index: " + date, + date.toString(), + "DATE_INDEX" + )); + return List.of(); } return new ArrayList<>(refs); } @@ -160,10 +171,11 @@ public class MemoryRuntime extends AbstractAgentModule.Standalone implements Sta private ActivatedMemorySlice buildActivatedMemorySlice(SliceRef ref) { MemoryUnit memoryUnit = memoryCapability.getMemoryUnit(ref.getUnitId()); - MemorySlice memorySlice = memoryCapability.getMemorySlice(ref.getUnitId(), ref.getSliceId()); - if (memoryUnit == null || memorySlice == null) { + Result memorySliceResult = memoryCapability.getMemorySlice(ref.getUnitId(), ref.getSliceId()); + if (memorySliceResult.exceptionOrNull() != null) { return null; } + MemorySlice memorySlice = memorySliceResult.getOrThrow(); List messages = sliceMessages(memoryUnit, memorySlice); LocalDate date = Instant.ofEpochMilli(memorySlice.getTimestamp()) .atZone(ZoneId.systemDefault()) @@ -254,7 +266,7 @@ public class MemoryRuntime extends AbstractAgentModule.Standalone implements Sta try { dateIndex.put(LocalDate.parse(date), decodeSliceRefs(dateObject.getJSONArray("refs"))); } catch (Exception e) { - log.warn("[MemoryRuntime] 跳过非法日期索引: {}", date, e); + log.warn("skip invalid date index: {}", date, e); } } } diff --git a/Partner-Core/src/main/java/work/slhaf/partner/module/memory/runtime/exception/MemoryLookupException.java b/Partner-Core/src/main/java/work/slhaf/partner/module/memory/runtime/exception/MemoryLookupException.java new file mode 100644 index 00000000..e3a42ae4 --- /dev/null +++ b/Partner-Core/src/main/java/work/slhaf/partner/module/memory/runtime/exception/MemoryLookupException.java @@ -0,0 +1,30 @@ +package work.slhaf.partner.module.memory.runtime.exception; + +import work.slhaf.partner.framework.agent.exception.AgentRuntimeException; +import work.slhaf.partner.framework.agent.exception.ExceptionReport; + +public class MemoryLookupException extends AgentRuntimeException { + + private final String lookupKey; + private final String lookupTarget; + + public MemoryLookupException(String message, String lookupKey, String lookupTarget) { + super(message); + this.lookupKey = lookupKey; + this.lookupTarget = lookupTarget; + } + + public MemoryLookupException(String message, String lookupKey, String lookupTarget, Throwable cause) { + super(message, cause); + this.lookupKey = lookupKey; + this.lookupTarget = lookupTarget; + } + + @Override + public ExceptionReport toReport() { + ExceptionReport report = super.toReport(); + report.getExtra().put("lookupKey", lookupKey); + report.getExtra().put("lookupTarget", lookupTarget); + return report; + } +} diff --git a/Partner-Core/src/main/java/work/slhaf/partner/module/memory/runtime/exception/UnExistedDateIndexException.java b/Partner-Core/src/main/java/work/slhaf/partner/module/memory/runtime/exception/UnExistedDateIndexException.java deleted file mode 100644 index 001cf1ad..00000000 --- a/Partner-Core/src/main/java/work/slhaf/partner/module/memory/runtime/exception/UnExistedDateIndexException.java +++ /dev/null @@ -1,7 +0,0 @@ -package work.slhaf.partner.module.memory.runtime.exception; - -public class UnExistedDateIndexException extends RuntimeException { - public UnExistedDateIndexException(String message) { - super(message); - } -} diff --git a/Partner-Core/src/main/java/work/slhaf/partner/module/memory/runtime/exception/UnExistedTopicException.java b/Partner-Core/src/main/java/work/slhaf/partner/module/memory/runtime/exception/UnExistedTopicException.java deleted file mode 100644 index 4769f5ff..00000000 --- a/Partner-Core/src/main/java/work/slhaf/partner/module/memory/runtime/exception/UnExistedTopicException.java +++ /dev/null @@ -1,7 +0,0 @@ -package work.slhaf.partner.module.memory.runtime.exception; - -public class UnExistedTopicException extends RuntimeException { - public UnExistedTopicException(String message) { - super(message); - } -} diff --git a/Partner-Core/src/main/java/work/slhaf/partner/module/memory/selector/MemorySelector.java b/Partner-Core/src/main/java/work/slhaf/partner/module/memory/selector/MemorySelector.java index 00350f24..c86cf628 100644 --- a/Partner-Core/src/main/java/work/slhaf/partner/module/memory/selector/MemorySelector.java +++ b/Partner-Core/src/main/java/work/slhaf/partner/module/memory/selector/MemorySelector.java @@ -16,8 +16,7 @@ import work.slhaf.partner.framework.agent.factory.component.abstracts.AbstractAg import work.slhaf.partner.framework.agent.factory.component.annotation.InjectModule; import work.slhaf.partner.framework.agent.model.pojo.Message; import work.slhaf.partner.module.memory.runtime.MemoryRuntime; -import work.slhaf.partner.module.memory.runtime.exception.UnExistedDateIndexException; -import work.slhaf.partner.module.memory.runtime.exception.UnExistedTopicException; +import work.slhaf.partner.module.memory.runtime.exception.MemoryLookupException; import work.slhaf.partner.module.memory.selector.evaluator.SliceSelectEvaluator; import work.slhaf.partner.module.memory.selector.evaluator.entity.EvaluatorInput; import work.slhaf.partner.module.memory.selector.extractor.MemorySelectExtractor; @@ -114,7 +113,7 @@ public class MemorySelector extends AbstractAgentModule.Running activatedSlices = selectAndEvaluateMemory(snapshotInputs, extractorResult); updateMemoryContext(activatedSlices); } @@ -207,7 +206,6 @@ public class MemorySelector extends AbstractAgentModule.Running selectAndEvaluateMemory(Map snapshotInputs, ExtractorResult extractorResult) { - log.debug("[MemorySelector] 触发记忆回溯..."); LinkedHashMap candidates = new LinkedHashMap<>(); setMemoryCandidates(candidates, extractorResult.getMatches()); EvaluatorInput evaluatorInput = EvaluatorInput.builder() @@ -231,7 +229,7 @@ public class MemorySelector extends AbstractAgentModule.Running { - log.debug("[MemorySelectExtractor] 主题提取结果: {}", value); - return value; - }, + extractorResult = result.onFailure(ExceptionReporterHandler.INSTANCE::report).fold( + value -> value, exception -> { - log.error("[MemorySelectExtractor] 主题提取出错: ", exception); ExtractorResult fallback = new ExtractorResult(); - fallback.setRecall(false); fallback.setMatches(List.of()); return fallback; } diff --git a/Partner-Core/src/main/java/work/slhaf/partner/module/memory/selector/extractor/entity/ExtractorResult.java b/Partner-Core/src/main/java/work/slhaf/partner/module/memory/selector/extractor/entity/ExtractorResult.java index c2ccc4cf..24fd11fb 100644 --- a/Partner-Core/src/main/java/work/slhaf/partner/module/memory/selector/extractor/entity/ExtractorResult.java +++ b/Partner-Core/src/main/java/work/slhaf/partner/module/memory/selector/extractor/entity/ExtractorResult.java @@ -6,6 +6,5 @@ import java.util.List; @Data public class ExtractorResult { - private boolean recall; private List matches; } diff --git a/Partner-Core/src/main/java/work/slhaf/partner/module/memory/updater/MemoryUpdater.java b/Partner-Core/src/main/java/work/slhaf/partner/module/memory/updater/MemoryUpdater.java index a713d122..fdc24741 100644 --- a/Partner-Core/src/main/java/work/slhaf/partner/module/memory/updater/MemoryUpdater.java +++ b/Partner-Core/src/main/java/work/slhaf/partner/module/memory/updater/MemoryUpdater.java @@ -1,6 +1,5 @@ package work.slhaf.partner.module.memory.updater; -import com.alibaba.fastjson2.JSONObject; import kotlin.Unit; import lombok.Data; import lombok.EqualsAndHashCode; @@ -25,7 +24,6 @@ import work.slhaf.partner.module.memory.runtime.MemoryRuntime; import work.slhaf.partner.module.memory.updater.summarizer.MultiSummarizer; import work.slhaf.partner.module.memory.updater.summarizer.SingleSummarizer; import work.slhaf.partner.module.memory.updater.summarizer.entity.SummarizeInput; -import work.slhaf.partner.module.memory.updater.summarizer.entity.SummarizeResult; import work.slhaf.partner.runtime.PartnerRunningFlowContext; import java.util.List; @@ -126,9 +124,7 @@ public class MemoryUpdater extends AbstractAgentModule.Running { + MemoryUnit memoryUnit = memoryCapability.updateMemoryUnit(chatSnapshot, summarizeResult.getSummary()); + memoryRuntime.recordMemory( + memoryUnit, + summarizeResult.getTopicPath(), + summarizeResult.getRelatedTopicPath() + ); + MemorySlice newSlice = memoryUnit.getSlices().getLast(); + return new RollingRecord(memoryUnit.getId(), newSlice.getId(), newSlice.getSummary()); + }, + exp -> { + MemoryUnit memoryUnit = memoryCapability.updateMemoryUnit(chatSnapshot, "no summary, due to exception"); + MemorySlice newSlice = memoryUnit.getSlices().getLast(); + return new RollingRecord(memoryUnit.getId(), newSlice.getId(), newSlice.getSummary()); + }); } @Override diff --git a/Partner-Core/src/main/java/work/slhaf/partner/module/memory/updater/summarizer/MultiSummarizer.java b/Partner-Core/src/main/java/work/slhaf/partner/module/memory/updater/summarizer/MultiSummarizer.java index 74d26baa..62555458 100644 --- a/Partner-Core/src/main/java/work/slhaf/partner/module/memory/updater/summarizer/MultiSummarizer.java +++ b/Partner-Core/src/main/java/work/slhaf/partner/module/memory/updater/summarizer/MultiSummarizer.java @@ -1,13 +1,14 @@ package work.slhaf.partner.module.memory.updater.summarizer; import cn.hutool.json.JSONUtil; -import com.alibaba.fastjson2.JSONObject; import lombok.Data; import lombok.EqualsAndHashCode; +import org.jetbrains.annotations.NotNull; import work.slhaf.partner.framework.agent.factory.component.abstracts.AbstractAgentModule; import work.slhaf.partner.framework.agent.factory.component.annotation.InjectModule; import work.slhaf.partner.framework.agent.model.ActivateModel; import work.slhaf.partner.framework.agent.model.pojo.Message; +import work.slhaf.partner.framework.agent.support.Result; import work.slhaf.partner.module.memory.runtime.MemoryRuntime; import work.slhaf.partner.module.memory.updater.summarizer.entity.SummarizeInput; import work.slhaf.partner.module.memory.updater.summarizer.entity.SummarizeResult; @@ -17,25 +18,22 @@ import java.util.List; @EqualsAndHashCode(callSuper = true) @Data -public class MultiSummarizer extends AbstractAgentModule.Sub implements ActivateModel { +public class MultiSummarizer extends AbstractAgentModule.Sub> implements ActivateModel { @InjectModule private MemoryRuntime memoryRuntime; @Override - public SummarizeResult execute(SummarizeInput input) { - log.debug("[MemorySummarizer] 整体摘要开始..."); - SummarizeResult result = formattedChat( + public @NotNull Result execute(SummarizeInput input) { + return formattedChat( List.of(new Message(Message.Character.USER, JSONUtil.toJsonPrettyStr(input))), SummarizeResult.class - ).getOrThrow(); - log.debug("[MemorySummarizer] 整体摘要结果: {}", JSONObject.toJSONString(result)); - return fix(result); + ).onSuccess(this::fix); } - private SummarizeResult fix(SummarizeResult result) { + private void fix(SummarizeResult result) { if (result == null || result.getTopicPath() == null || result.getTopicPath().isEmpty()) { - return result; + return; } String topicPath = memoryRuntime.fixTopicPath(result.getTopicPath()); List relatedTopicPath = new ArrayList<>(); @@ -44,9 +42,9 @@ public class MultiSummarizer extends AbstractAgentModule.Sub, Voi int index = i; executor.execute(() -> { try { - String summarized = singleExecute(JSONObject.of("content", content).toString()); + String summarized = chat(List.of(new Message(Message.Character.USER, content))).onFailure(ExceptionReporterHandler.INSTANCE::report).fold( + res -> res, + exp -> content + ); chatMessages.set(index, new Message(Message.Character.ASSISTANT, summarized)); } finally { latch.countDown(); @@ -59,12 +62,7 @@ public class SingleSummarizer extends AbstractAgentModule.Sub, Voi } private String singleExecute(String primaryContent) { - try { - return chat(List.of(new Message(Message.Character.USER, primaryContent))).getOrThrow(); - } catch (Exception e) { - log.error("[SingleSummarizer] 单消息总结出错: ", e); - return primaryContent; - } + return chat(List.of(new Message(Message.Character.USER, primaryContent))).getOrThrow(); } @Override diff --git a/Partner-Core/src/test/java/work/slhaf/partner/core/memory/MemoryCoreTest.java b/Partner-Core/src/test/java/work/slhaf/partner/core/memory/MemoryCoreTest.java index 86e2f1a8..2f11c838 100644 --- a/Partner-Core/src/test/java/work/slhaf/partner/core/memory/MemoryCoreTest.java +++ b/Partner-Core/src/test/java/work/slhaf/partner/core/memory/MemoryCoreTest.java @@ -77,7 +77,7 @@ class MemoryCoreTest { assertEquals("second-summary", appendedSlice.getSummary()); assertTrue(appendedSlice.getTimestamp() > 0); - MemorySlice loadedSlice = memoryCore.getMemorySlice(sessionId, appendedSlice.getId()); + MemorySlice loadedSlice = memoryCore.getMemorySlice(sessionId, appendedSlice.getId()).getOrThrow(); assertNotNull(loadedSlice); assertEquals(1, loadedSlice.getStartIndex()); assertEquals(3, loadedSlice.getEndIndex()); diff --git a/Partner-Core/src/test/java/work/slhaf/partner/module/memory/runtime/MemoryRuntimeTest.java b/Partner-Core/src/test/java/work/slhaf/partner/module/memory/runtime/MemoryRuntimeTest.java index 6ac884cf..7dcd7da3 100644 --- a/Partner-Core/src/test/java/work/slhaf/partner/module/memory/runtime/MemoryRuntimeTest.java +++ b/Partner-Core/src/test/java/work/slhaf/partner/module/memory/runtime/MemoryRuntimeTest.java @@ -12,6 +12,8 @@ import work.slhaf.partner.core.memory.pojo.MemorySlice; import work.slhaf.partner.core.memory.pojo.MemoryUnit; import work.slhaf.partner.core.memory.pojo.SliceRef; import work.slhaf.partner.framework.agent.model.pojo.Message; +import work.slhaf.partner.framework.agent.support.Result; +import work.slhaf.partner.module.memory.runtime.exception.MemoryLookupException; import work.slhaf.partner.module.memory.selector.ActivatedMemorySlice; import java.lang.reflect.Field; @@ -26,8 +28,7 @@ import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; class MemoryRuntimeTest { @@ -204,6 +205,27 @@ class MemoryRuntimeTest { assertEquals("second", dateResult.getFirst().getSummary()); } + @Test + void shouldThrowMemoryLookupExceptionWhenTopicOrDateIndexDoesNotExist() throws Exception { + StubMemoryCapability memoryCapability = new StubMemoryCapability("session-test"); + MemoryRuntime runtime = new MemoryRuntime(); + setField(runtime, "memoryCapability", memoryCapability); + setField(runtime, "cognitionCapability", stubCognitionCapability(List.of(message("seed")))); + + MemoryLookupException topicException = assertThrows( + MemoryLookupException.class, + () -> runtime.queryActivatedMemoryByTopicPath("topic/missing") + ); + assertEquals("不存在的主题: topic/missing", topicException.getMessage()); + + MemoryLookupException dateException = assertThrows( + MemoryLookupException.class, + () -> runtime.queryActivatedMemoryByDate(LocalDate.parse("1970-01-01")) + ); + assertEquals("不存在的日期索引: 1970-01-01", dateException.getMessage()); + assertInstanceOf(MemoryLookupException.class, dateException); + } + private static final class StubMemoryCapability implements MemoryCapability { private final String sessionId; private final Map units = new HashMap<>(); @@ -222,15 +244,24 @@ class MemoryRuntimeTest { } @Override - public MemorySlice getMemorySlice(String unitId, String sliceId) { + public Result getMemorySlice(String unitId, String sliceId) { MemoryUnit unit = units.get(unitId); if (unit == null || unit.getSlices() == null) { - return null; + return Result.failure(new MemoryLookupException( + "Memory slice not found: " + unitId + ":" + sliceId, + unitId + ":" + sliceId, + "MEMORY_SLICE" + )); } return unit.getSlices().stream() .filter(slice -> sliceId.equals(slice.getId())) .findFirst() - .orElse(null); + .map(Result::success) + .orElseGet(() -> Result.failure(new MemoryLookupException( + "Memory slice not found: " + unitId + ":" + sliceId, + unitId + ":" + sliceId, + "MEMORY_SLICE" + ))); } @Override diff --git a/Partner-Core/src/test/java/work/slhaf/partner/module/memory/updater/MemoryUpdaterTest.java b/Partner-Core/src/test/java/work/slhaf/partner/module/memory/updater/MemoryUpdaterTest.java index 6591bed1..bd651818 100644 --- a/Partner-Core/src/test/java/work/slhaf/partner/module/memory/updater/MemoryUpdaterTest.java +++ b/Partner-Core/src/test/java/work/slhaf/partner/module/memory/updater/MemoryUpdaterTest.java @@ -8,7 +8,9 @@ import work.slhaf.partner.core.memory.MemoryCapability; import work.slhaf.partner.core.memory.pojo.MemorySlice; import work.slhaf.partner.core.memory.pojo.MemoryUnit; import work.slhaf.partner.framework.agent.model.pojo.Message; +import work.slhaf.partner.framework.agent.support.Result; import work.slhaf.partner.module.memory.runtime.MemoryRuntime; +import work.slhaf.partner.module.memory.runtime.exception.MemoryLookupException; import work.slhaf.partner.module.memory.updater.summarizer.MultiSummarizer; import work.slhaf.partner.module.memory.updater.summarizer.SingleSummarizer; import work.slhaf.partner.module.memory.updater.summarizer.entity.SummarizeResult; @@ -84,9 +86,9 @@ class MemoryUpdaterTest { setField(updater, "singleSummarizer", singleSummarizer); when(memoryRuntime.getTopicTree()).thenReturn("topic-tree"); - when(multiSummarizer.execute(Mockito.any())).thenReturn( + when(multiSummarizer.execute(Mockito.any())).thenReturn(Result.success( summarizeResult("new-summary", "topic/main", List.of("topic/related")) - ); + )); MemoryUnit existingUnit = new MemoryUnit("session-1"); existingUnit.getConversationMessages().addAll(List.of( @@ -134,9 +136,9 @@ class MemoryUpdaterTest { setField(updater, "singleSummarizer", singleSummarizer); when(memoryRuntime.getTopicTree()).thenReturn("topic-tree"); - when(multiSummarizer.execute(Mockito.any())).thenReturn( + when(multiSummarizer.execute(Mockito.any())).thenReturn(Result.success( summarizeResult("fresh-summary", "topic/root", List.of()) - ); + )); Object rollingRecord = invokeUpdateMemory(updater, List.of( message(Message.Character.USER, "first"), @@ -258,15 +260,24 @@ class MemoryUpdaterTest { } @Override - public MemorySlice getMemorySlice(String unitId, String sliceId) { + public Result getMemorySlice(String unitId, String sliceId) { MemoryUnit unit = units.get(unitId); if (unit == null || unit.getSlices() == null) { - return null; + return Result.failure(new MemoryLookupException( + "Memory slice not found: " + unitId + ":" + sliceId, + unitId + ":" + sliceId, + "MEMORY_SLICE" + )); } return unit.getSlices().stream() .filter(slice -> sliceId.equals(slice.getId())) .findFirst() - .orElse(null); + .map(Result::success) + .orElseGet(() -> Result.failure(new MemoryLookupException( + "Memory slice not found: " + unitId + ":" + sliceId, + unitId + ":" + sliceId, + "MEMORY_SLICE" + ))); } @Override