mirror of
https://github.com/slhaf/Partner.git
synced 2026-10-25 19:03:48 +00:00
refactor(memory): snapshot rolling results
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
package work.slhaf.partner.core.memory;
|
package work.slhaf.partner.core.memory;
|
||||||
|
|
||||||
import work.slhaf.partner.core.memory.pojo.MemorySlice;
|
import work.slhaf.partner.core.memory.pojo.MemorySliceSnapshot;
|
||||||
import work.slhaf.partner.core.memory.pojo.MemoryUnit;
|
import work.slhaf.partner.core.memory.pojo.MemoryUnitSnapshot;
|
||||||
import work.slhaf.partner.framework.agent.factory.capability.annotation.Capability;
|
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.model.pojo.Message;
|
||||||
import work.slhaf.partner.framework.agent.support.Result;
|
import work.slhaf.partner.framework.agent.support.Result;
|
||||||
@@ -12,13 +12,13 @@ import java.util.List;
|
|||||||
@Capability(value = "memory")
|
@Capability(value = "memory")
|
||||||
public interface MemoryCapability {
|
public interface MemoryCapability {
|
||||||
|
|
||||||
MemoryUnit getMemoryUnit(String unitId);
|
MemoryUnitSnapshot getMemoryUnit(String unitId);
|
||||||
|
|
||||||
Result<MemorySlice> getMemorySlice(String unitId, String sliceId);
|
Result<MemorySliceSnapshot> getMemorySlice(String unitId, String sliceId);
|
||||||
|
|
||||||
MemoryUnit updateMemoryUnit(List<Message> chatMessages, String summary);
|
MemoryUnitSnapshot updateMemoryUnit(List<Message> chatMessages, String summary);
|
||||||
|
|
||||||
Collection<MemoryUnit> listMemoryUnits();
|
Collection<MemoryUnitSnapshot> listMemoryUnits();
|
||||||
|
|
||||||
void refreshMemorySession();
|
void refreshMemorySession();
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ import com.alibaba.fastjson2.JSONObject;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import work.slhaf.partner.core.memory.pojo.MemorySlice;
|
import work.slhaf.partner.core.memory.pojo.MemorySlice;
|
||||||
|
import work.slhaf.partner.core.memory.pojo.MemorySliceSnapshot;
|
||||||
import work.slhaf.partner.core.memory.pojo.MemoryUnit;
|
import work.slhaf.partner.core.memory.pojo.MemoryUnit;
|
||||||
|
import work.slhaf.partner.core.memory.pojo.MemoryUnitSnapshot;
|
||||||
import work.slhaf.partner.framework.agent.factory.capability.annotation.CapabilityCore;
|
import work.slhaf.partner.framework.agent.factory.capability.annotation.CapabilityCore;
|
||||||
import work.slhaf.partner.framework.agent.factory.capability.annotation.CapabilityMethod;
|
import work.slhaf.partner.framework.agent.factory.capability.annotation.CapabilityMethod;
|
||||||
import work.slhaf.partner.framework.agent.model.pojo.Message;
|
import work.slhaf.partner.framework.agent.model.pojo.Message;
|
||||||
@@ -36,10 +38,10 @@ public class MemoryCore implements StateSerializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@CapabilityMethod
|
@CapabilityMethod
|
||||||
public MemoryUnit updateMemoryUnit(List<Message> chatMessages, String summary) {
|
public MemoryUnitSnapshot updateMemoryUnit(List<Message> chatMessages, String summary) {
|
||||||
memoryLock.lock();
|
memoryLock.lock();
|
||||||
try {
|
try {
|
||||||
MemoryUnit unit = getMemoryUnit(memorySessionId);
|
MemoryUnit unit = getOrLoadMemoryUnit(memorySessionId);
|
||||||
unit.updateTimestamp();
|
unit.updateTimestamp();
|
||||||
|
|
||||||
List<Message> conversationMessages = unit.getConversationMessages();
|
List<Message> conversationMessages = unit.getConversationMessages();
|
||||||
@@ -55,14 +57,60 @@ public class MemoryCore implements StateSerializable {
|
|||||||
|
|
||||||
unit.getSlices().add(memorySlice);
|
unit.getSlices().add(memorySlice);
|
||||||
normalizeMemoryUnit(unit);
|
normalizeMemoryUnit(unit);
|
||||||
return unit;
|
return unit.snapshot();
|
||||||
} finally {
|
} finally {
|
||||||
memoryLock.unlock();
|
memoryLock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@CapabilityMethod
|
@CapabilityMethod
|
||||||
public MemoryUnit getMemoryUnit(String unitId) {
|
public MemoryUnitSnapshot getMemoryUnit(String unitId) {
|
||||||
|
memoryLock.lock();
|
||||||
|
try {
|
||||||
|
MemoryUnit unit = getOrLoadMemoryUnit(unitId);
|
||||||
|
normalizeMemoryUnit(unit);
|
||||||
|
return unit.snapshot();
|
||||||
|
} finally {
|
||||||
|
memoryLock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@CapabilityMethod
|
||||||
|
public Result<MemorySliceSnapshot> getMemorySlice(String unitId, String sliceId) {
|
||||||
|
memoryLock.lock();
|
||||||
|
try {
|
||||||
|
MemoryUnit memoryUnit = memoryUnits.get(unitId);
|
||||||
|
if (memoryUnit == null) {
|
||||||
|
return memorySliceNotFound(unitId, sliceId);
|
||||||
|
}
|
||||||
|
memoryUnit.load();
|
||||||
|
normalizeMemoryUnit(memoryUnit);
|
||||||
|
for (MemorySlice slice : memoryUnit.getSlices()) {
|
||||||
|
if (sliceId.equals(slice.getId())) {
|
||||||
|
return Result.success(slice.snapshot());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return memorySliceNotFound(unitId, sliceId);
|
||||||
|
} finally {
|
||||||
|
memoryLock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@CapabilityMethod
|
||||||
|
public Collection<MemoryUnitSnapshot> listMemoryUnits() {
|
||||||
|
memoryLock.lock();
|
||||||
|
try {
|
||||||
|
return memoryUnits.values().stream()
|
||||||
|
.peek(MemoryUnit::load)
|
||||||
|
.peek(this::normalizeMemoryUnit)
|
||||||
|
.map(MemoryUnit::snapshot)
|
||||||
|
.toList();
|
||||||
|
} finally {
|
||||||
|
memoryLock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private MemoryUnit getOrLoadMemoryUnit(String unitId) {
|
||||||
MemoryUnit unit = memoryUnits.computeIfAbsent(unitId, id -> {
|
MemoryUnit unit = memoryUnits.computeIfAbsent(unitId, id -> {
|
||||||
MemoryUnit newUnit = new MemoryUnit(id);
|
MemoryUnit newUnit = new MemoryUnit(id);
|
||||||
newUnit.register();
|
newUnit.register();
|
||||||
@@ -72,21 +120,7 @@ public class MemoryCore implements StateSerializable {
|
|||||||
return unit;
|
return unit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@CapabilityMethod
|
private Result<MemorySliceSnapshot> memorySliceNotFound(String unitId, String sliceId) {
|
||||||
public Result<MemorySlice> getMemorySlice(String unitId, String sliceId) {
|
|
||||||
MemoryUnit memoryUnit = memoryUnits.get(unitId);
|
|
||||||
if (memoryUnit == 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 Result.success(slice);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Result.failure(new MemoryLookupException(
|
return Result.failure(new MemoryLookupException(
|
||||||
"Memory slice not found: " + unitId + ":" + sliceId,
|
"Memory slice not found: " + unitId + ":" + sliceId,
|
||||||
unitId + ":" + sliceId,
|
unitId + ":" + sliceId,
|
||||||
@@ -94,11 +128,6 @@ public class MemoryCore implements StateSerializable {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@CapabilityMethod
|
|
||||||
public Collection<MemoryUnit> listMemoryUnits() {
|
|
||||||
return new ArrayList<>(memoryUnits.values());
|
|
||||||
}
|
|
||||||
|
|
||||||
@CapabilityMethod
|
@CapabilityMethod
|
||||||
public void refreshMemorySession() {
|
public void refreshMemorySession() {
|
||||||
memorySessionId = UUID.randomUUID().toString();
|
memorySessionId = UUID.randomUUID().toString();
|
||||||
|
|||||||
@@ -33,6 +33,16 @@ public class MemorySlice implements Comparable<MemorySlice> {
|
|||||||
return new MemorySlice(id, startIndex, endIndex, summary, timestamp);
|
return new MemorySlice(id, startIndex, endIndex, summary, timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MemorySliceSnapshot snapshot() {
|
||||||
|
return new MemorySliceSnapshot(
|
||||||
|
id,
|
||||||
|
startIndex == null ? 0 : startIndex,
|
||||||
|
endIndex == null ? 0 : endIndex,
|
||||||
|
summary,
|
||||||
|
timestamp == null ? 0L : timestamp
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(MemorySlice memorySlice) {
|
public int compareTo(MemorySlice memorySlice) {
|
||||||
if (memorySlice.getTimestamp() > this.getTimestamp()) {
|
if (memorySlice.getTimestamp() > this.getTimestamp()) {
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package work.slhaf.partner.core.memory.pojo
|
||||||
|
|
||||||
|
data class MemorySliceSnapshot(
|
||||||
|
val id: String,
|
||||||
|
val startIndex: Int,
|
||||||
|
val endIndex: Int,
|
||||||
|
val summary: String?,
|
||||||
|
val timestamp: Long,
|
||||||
|
)
|
||||||
@@ -31,6 +31,15 @@ public class MemoryUnit implements StateSerializable {
|
|||||||
timestamp = System.currentTimeMillis();
|
timestamp = System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MemoryUnitSnapshot snapshot() {
|
||||||
|
return new MemoryUnitSnapshot(
|
||||||
|
id,
|
||||||
|
List.copyOf(conversationMessages),
|
||||||
|
timestamp == null ? 0L : timestamp,
|
||||||
|
slices.stream().map(MemorySlice::snapshot).toList()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull Path statePath() {
|
public @NotNull Path statePath() {
|
||||||
return Path.of("core", "memory", "memory-unit" + id + ".json");
|
return Path.of("core", "memory", "memory-unit" + id + ".json");
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package work.slhaf.partner.core.memory.pojo
|
||||||
|
|
||||||
|
import work.slhaf.partner.framework.agent.model.pojo.Message
|
||||||
|
|
||||||
|
data class MemoryUnitSnapshot(
|
||||||
|
val id: String,
|
||||||
|
val conversationMessages: List<Message>,
|
||||||
|
val timestamp: Long,
|
||||||
|
val slices: List<MemorySliceSnapshot>,
|
||||||
|
) {
|
||||||
|
|
||||||
|
fun messagesOf(slice: MemorySliceSnapshot): List<Message> {
|
||||||
|
if (conversationMessages.isEmpty()) {
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
val start = slice.startIndex.coerceIn(0, conversationMessages.size)
|
||||||
|
val end = slice.endIndex.coerceIn(start, conversationMessages.size)
|
||||||
|
if (start >= end) {
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
return conversationMessages.subList(start, end).toList()
|
||||||
|
}
|
||||||
|
}
|
||||||
+7
-7
@@ -10,8 +10,8 @@ import work.slhaf.partner.core.cognition.CognitionCapability;
|
|||||||
import work.slhaf.partner.core.cognition.context.BlockContent;
|
import work.slhaf.partner.core.cognition.context.BlockContent;
|
||||||
import work.slhaf.partner.core.cognition.context.ContextBlock;
|
import work.slhaf.partner.core.cognition.context.ContextBlock;
|
||||||
import work.slhaf.partner.core.memory.MemoryCapability;
|
import work.slhaf.partner.core.memory.MemoryCapability;
|
||||||
import work.slhaf.partner.core.memory.pojo.MemorySlice;
|
import work.slhaf.partner.core.memory.pojo.MemorySliceSnapshot;
|
||||||
import work.slhaf.partner.core.memory.pojo.MemoryUnit;
|
import work.slhaf.partner.core.memory.pojo.MemoryUnitSnapshot;
|
||||||
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.Init;
|
||||||
@@ -75,16 +75,16 @@ class BuiltinCapabilityActionProvider implements BuiltinActionProvider {
|
|||||||
Function<Map<String, Object>, String> invoker = params -> {
|
Function<Map<String, Object>, String> invoker = params -> {
|
||||||
String unitId = BuiltinActionRegistry.BuiltinActionDefinition.requireString(params, "unit_id");
|
String unitId = BuiltinActionRegistry.BuiltinActionDefinition.requireString(params, "unit_id");
|
||||||
String sliceId = BuiltinActionRegistry.BuiltinActionDefinition.requireString(params, "slice_id");
|
String sliceId = BuiltinActionRegistry.BuiltinActionDefinition.requireString(params, "slice_id");
|
||||||
Result<MemorySlice> sliceResult = memoryCapability.getMemorySlice(unitId, sliceId);
|
Result<MemorySliceSnapshot> sliceResult = memoryCapability.getMemorySlice(unitId, sliceId);
|
||||||
if (sliceResult.exceptionOrNull() != null) {
|
if (sliceResult.exceptionOrNull() != null) {
|
||||||
return JSONObject.of(
|
return JSONObject.of(
|
||||||
"ok", false,
|
"ok", false,
|
||||||
"message", sliceResult.exceptionOrNull().getLocalizedMessage()
|
"message", sliceResult.exceptionOrNull().getLocalizedMessage()
|
||||||
).toJSONString();
|
).toJSONString();
|
||||||
}
|
}
|
||||||
MemorySlice slice = sliceResult.getOrThrow();
|
MemorySliceSnapshot slice = sliceResult.getOrThrow();
|
||||||
|
|
||||||
MemoryUnit unit = memoryCapability.getMemoryUnit(unitId);
|
MemoryUnitSnapshot unit = memoryCapability.getMemoryUnit(unitId);
|
||||||
cognitionCapability.contextWorkspace().register(new ContextBlock(
|
cognitionCapability.contextWorkspace().register(new ContextBlock(
|
||||||
buildMemoryRecallFullBlock(unit, slice),
|
buildMemoryRecallFullBlock(unit, slice),
|
||||||
Set.of(ContextBlock.FocusedDomain.MEMORY),
|
Set.of(ContextBlock.FocusedDomain.MEMORY),
|
||||||
@@ -105,13 +105,13 @@ class BuiltinCapabilityActionProvider implements BuiltinActionProvider {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private @NotNull BlockContent buildMemoryRecallFullBlock(MemoryUnit unit, MemorySlice slice) {
|
private @NotNull BlockContent buildMemoryRecallFullBlock(MemoryUnitSnapshot unit, MemorySliceSnapshot slice) {
|
||||||
return new BlockContent("memory_recall", "memory_capability") {
|
return new BlockContent("memory_recall", "memory_capability") {
|
||||||
@Override
|
@Override
|
||||||
protected void fillXml(@NotNull Document document, @NotNull Element root) {
|
protected void fillXml(@NotNull Document document, @NotNull Element root) {
|
||||||
root.setAttribute("unit_id", unit.getId());
|
root.setAttribute("unit_id", unit.getId());
|
||||||
root.setAttribute("slice_id", slice.getId());
|
root.setAttribute("slice_id", slice.getId());
|
||||||
appendRepeatedElements(document, root, "message", unit.getConversationMessages().subList(slice.getStartIndex(), slice.getEndIndex()), (messageElement, message) -> {
|
appendRepeatedElements(document, root, "message", unit.messagesOf(slice), (messageElement, message) -> {
|
||||||
messageElement.setAttribute("role", message.getRole().name().toLowerCase(Locale.ROOT));
|
messageElement.setAttribute("role", message.getRole().name().toLowerCase(Locale.ROOT));
|
||||||
messageElement.setTextContent(message.getContent());
|
messageElement.setTextContent(message.getContent());
|
||||||
return Unit.INSTANCE;
|
return Unit.INSTANCE;
|
||||||
|
|||||||
+12
-10
@@ -13,8 +13,8 @@ import work.slhaf.partner.core.cognition.CognitionCapability;
|
|||||||
import work.slhaf.partner.core.cognition.context.BlockContent;
|
import work.slhaf.partner.core.cognition.context.BlockContent;
|
||||||
import work.slhaf.partner.core.cognition.context.ContextBlock;
|
import work.slhaf.partner.core.cognition.context.ContextBlock;
|
||||||
import work.slhaf.partner.core.memory.MemoryCapability;
|
import work.slhaf.partner.core.memory.MemoryCapability;
|
||||||
import work.slhaf.partner.core.memory.pojo.MemorySlice;
|
import work.slhaf.partner.core.memory.pojo.MemorySliceSnapshot;
|
||||||
import work.slhaf.partner.core.memory.pojo.MemoryUnit;
|
import work.slhaf.partner.core.memory.pojo.MemoryUnitSnapshot;
|
||||||
import work.slhaf.partner.core.perceive.PerceiveCapability;
|
import work.slhaf.partner.core.perceive.PerceiveCapability;
|
||||||
import work.slhaf.partner.framework.agent.exception.AgentRuntimeException;
|
import work.slhaf.partner.framework.agent.exception.AgentRuntimeException;
|
||||||
import work.slhaf.partner.framework.agent.exception.ExceptionReporterHandler;
|
import work.slhaf.partner.framework.agent.exception.ExceptionReporterHandler;
|
||||||
@@ -31,6 +31,7 @@ import work.slhaf.partner.runtime.PartnerRunningFlowContext;
|
|||||||
|
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
@@ -140,7 +141,7 @@ public class DialogRolling extends AbstractAgentModule.Running<PartnerRunningFlo
|
|||||||
if (memoryId.isBlank()) {
|
if (memoryId.isBlank()) {
|
||||||
return fullChatSnapshot;
|
return fullChatSnapshot;
|
||||||
}
|
}
|
||||||
MemoryUnit existingUnit = memoryCapability.getMemoryUnit(memoryId);
|
MemoryUnitSnapshot existingUnit = memoryCapability.getMemoryUnit(memoryId);
|
||||||
if (existingUnit.getConversationMessages().isEmpty()) {
|
if (existingUnit.getConversationMessages().isEmpty()) {
|
||||||
return fullChatSnapshot;
|
return fullChatSnapshot;
|
||||||
}
|
}
|
||||||
@@ -158,8 +159,9 @@ public class DialogRolling extends AbstractAgentModule.Running<PartnerRunningFlo
|
|||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
RollingResult buildRollingResult(List<Message> chatSnapshot, int rollingSize, int retainDivisor) {
|
RollingResult buildRollingResult(List<Message> chatSnapshot, int rollingSize, int retainDivisor) {
|
||||||
messageCompressor.execute(chatSnapshot);
|
List<Message> rollingMessages = new ArrayList<>(chatSnapshot);
|
||||||
Result<String> summaryResult = messageSummarizer.execute(chatSnapshot);
|
messageCompressor.execute(rollingMessages);
|
||||||
|
Result<String> summaryResult = messageSummarizer.execute(rollingMessages);
|
||||||
String summary = summaryResult.fold(
|
String summary = summaryResult.fold(
|
||||||
value -> value,
|
value -> value,
|
||||||
exp -> "no summary, due to exception"
|
exp -> "no summary, due to exception"
|
||||||
@@ -167,20 +169,20 @@ public class DialogRolling extends AbstractAgentModule.Running<PartnerRunningFlo
|
|||||||
if (summary.isBlank()) {
|
if (summary.isBlank()) {
|
||||||
summary = "no summary, due to empty summarize result";
|
summary = "no summary, due to empty summarize result";
|
||||||
}
|
}
|
||||||
MemoryUnit memoryUnit = memoryCapability.updateMemoryUnit(chatSnapshot, summary);
|
MemoryUnitSnapshot memoryUnit = memoryCapability.updateMemoryUnit(rollingMessages, summary);
|
||||||
MemorySlice newSlice = memoryUnit.getSlices().getLast();
|
MemorySliceSnapshot newSlice = memoryUnit.getSlices().getLast();
|
||||||
return new RollingResult(memoryUnit, newSlice, List.copyOf(chatSnapshot), newSlice.getSummary(), rollingSize, retainDivisor);
|
return new RollingResult(memoryUnit, newSlice, rollingSize, retainDivisor);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyRolling(RollingResult result) {
|
private void applyRolling(RollingResult result) {
|
||||||
cognitionCapability.contextWorkspace().register(new ContextBlock(
|
cognitionCapability.contextWorkspace().register(new ContextBlock(
|
||||||
buildDialogAbstractBlock(result.summary(), result.memoryUnit().getId(), result.memorySlice().getId()),
|
buildDialogAbstractBlock(result.getSummary(), result.getMemoryUnit().getId(), result.getMemorySlice().getId()),
|
||||||
Set.of(ContextBlock.FocusedDomain.MEMORY, ContextBlock.FocusedDomain.COMMUNICATION),
|
Set.of(ContextBlock.FocusedDomain.MEMORY, ContextBlock.FocusedDomain.COMMUNICATION),
|
||||||
20,
|
20,
|
||||||
5,
|
5,
|
||||||
10
|
10
|
||||||
));
|
));
|
||||||
cognitionCapability.rollChatMessagesWithSnapshot(result.rollingSize(), result.retainDivisor());
|
cognitionCapability.rollChatMessagesWithSnapshot(result.getRollingSize(), result.getRetainDivisor());
|
||||||
}
|
}
|
||||||
|
|
||||||
private @NotNull BlockContent buildDialogAbstractBlock(String summary, String unitId, String sliceId) {
|
private @NotNull BlockContent buildDialogAbstractBlock(String summary, String unitId, String sliceId) {
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
package work.slhaf.partner.module.communication;
|
|
||||||
|
|
||||||
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 java.util.List;
|
|
||||||
|
|
||||||
public record RollingResult(
|
|
||||||
MemoryUnit memoryUnit,
|
|
||||||
MemorySlice memorySlice,
|
|
||||||
List<Message> incrementMessages,
|
|
||||||
String summary,
|
|
||||||
int rollingSize,
|
|
||||||
int retainDivisor
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package work.slhaf.partner.module.communication
|
||||||
|
|
||||||
|
import work.slhaf.partner.core.memory.pojo.MemorySliceSnapshot
|
||||||
|
import work.slhaf.partner.core.memory.pojo.MemoryUnitSnapshot
|
||||||
|
import work.slhaf.partner.framework.agent.model.pojo.Message
|
||||||
|
|
||||||
|
data class RollingResult(
|
||||||
|
val memoryUnit: MemoryUnitSnapshot,
|
||||||
|
val memorySlice: MemorySliceSnapshot,
|
||||||
|
val rollingSize: Int,
|
||||||
|
val retainDivisor: Int,
|
||||||
|
) {
|
||||||
|
val summary: String
|
||||||
|
get() = memorySlice.summary ?: ""
|
||||||
|
|
||||||
|
fun incrementMessages(): List<Message> = memoryUnit.messagesOf(memorySlice)
|
||||||
|
}
|
||||||
+8
-21
@@ -4,8 +4,8 @@ import com.alibaba.fastjson2.JSONObject;
|
|||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import work.slhaf.partner.core.cognition.CognitionCapability;
|
import work.slhaf.partner.core.cognition.CognitionCapability;
|
||||||
import work.slhaf.partner.core.memory.MemoryCapability;
|
import work.slhaf.partner.core.memory.MemoryCapability;
|
||||||
import work.slhaf.partner.core.memory.pojo.MemorySlice;
|
import work.slhaf.partner.core.memory.pojo.MemorySliceSnapshot;
|
||||||
import work.slhaf.partner.core.memory.pojo.MemoryUnit;
|
import work.slhaf.partner.core.memory.pojo.MemoryUnitSnapshot;
|
||||||
import work.slhaf.partner.core.memory.pojo.SliceRef;
|
import work.slhaf.partner.core.memory.pojo.SliceRef;
|
||||||
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;
|
||||||
@@ -52,11 +52,11 @@ public class MemoryRuntime extends AbstractAgentModule.Standalone implements Sta
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void recordMemory(MemoryUnit memoryUnit,
|
public void recordMemory(MemoryUnitSnapshot memoryUnit,
|
||||||
String topicPath,
|
String topicPath,
|
||||||
List<String> relatedTopicPaths,
|
List<String> relatedTopicPaths,
|
||||||
ActivationProfile activationProfile) {
|
ActivationProfile activationProfile) {
|
||||||
MemorySlice memorySlice = memoryUnit.getSlices().getLast();
|
MemorySliceSnapshot memorySlice = memoryUnit.getSlices().getLast();
|
||||||
SliceRef sliceRef = new SliceRef(memoryUnit.getId(), memorySlice.getId());
|
SliceRef sliceRef = new SliceRef(memoryUnit.getId(), memorySlice.getId());
|
||||||
LocalDate date = toLocalDate(memorySlice.getTimestamp());
|
LocalDate date = toLocalDate(memorySlice.getTimestamp());
|
||||||
runtimeLock.lock();
|
runtimeLock.lock();
|
||||||
@@ -159,13 +159,13 @@ public class MemoryRuntime extends AbstractAgentModule.Standalone implements Sta
|
|||||||
}
|
}
|
||||||
|
|
||||||
private ActivatedMemorySlice buildActivatedMemorySlice(SliceRef ref) {
|
private ActivatedMemorySlice buildActivatedMemorySlice(SliceRef ref) {
|
||||||
MemoryUnit memoryUnit = memoryCapability.getMemoryUnit(ref.getUnitId());
|
MemoryUnitSnapshot memoryUnit = memoryCapability.getMemoryUnit(ref.getUnitId());
|
||||||
Result<MemorySlice> memorySliceResult = memoryCapability.getMemorySlice(ref.getUnitId(), ref.getSliceId());
|
Result<MemorySliceSnapshot> memorySliceResult = memoryCapability.getMemorySlice(ref.getUnitId(), ref.getSliceId());
|
||||||
if (memoryUnit == null || memorySliceResult.exceptionOrNull() != null) {
|
if (memoryUnit == null || memorySliceResult.exceptionOrNull() != null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
MemorySlice memorySlice = memorySliceResult.getOrThrow();
|
MemorySliceSnapshot memorySlice = memorySliceResult.getOrThrow();
|
||||||
List<Message> messages = sliceMessages(memoryUnit, memorySlice);
|
List<Message> messages = memoryUnit.messagesOf(memorySlice);
|
||||||
LocalDate date = toLocalDate(memorySlice.getTimestamp());
|
LocalDate date = toLocalDate(memorySlice.getTimestamp());
|
||||||
return ActivatedMemorySlice.builder()
|
return ActivatedMemorySlice.builder()
|
||||||
.unitId(ref.getUnitId())
|
.unitId(ref.getUnitId())
|
||||||
@@ -177,19 +177,6 @@ public class MemoryRuntime extends AbstractAgentModule.Standalone implements Sta
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Message> sliceMessages(MemoryUnit memoryUnit, MemorySlice memorySlice) {
|
|
||||||
List<Message> conversationMessages = memoryUnit.getConversationMessages();
|
|
||||||
if (conversationMessages.isEmpty()) {
|
|
||||||
return List.of();
|
|
||||||
}
|
|
||||||
int size = conversationMessages.size();
|
|
||||||
int start = Math.clamp(memorySlice.getStartIndex(), 0, size);
|
|
||||||
int end = Math.clamp(memorySlice.getEndIndex(), start, size);
|
|
||||||
if (start >= end) {
|
|
||||||
return List.of();
|
|
||||||
}
|
|
||||||
return new ArrayList<>(conversationMessages.subList(start, end));
|
|
||||||
}
|
|
||||||
|
|
||||||
private LocalDate toLocalDate(Long timestamp) {
|
private LocalDate toLocalDate(Long timestamp) {
|
||||||
return Instant.ofEpochMilli(timestamp)
|
return Instant.ofEpochMilli(timestamp)
|
||||||
|
|||||||
+4
-14
@@ -149,7 +149,7 @@ public class MemoryRecallProfileExtractor extends AbstractAgentModule.Standalone
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void consume(RollingResult result) {
|
public void consume(RollingResult result) {
|
||||||
List<Message> slicedMessages = sliceMessages(result);
|
List<Message> slicedMessages = result.incrementMessages();
|
||||||
if (slicedMessages.isEmpty()) {
|
if (slicedMessages.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -169,31 +169,21 @@ public class MemoryRecallProfileExtractor extends AbstractAgentModule.Standalone
|
|||||||
relatedTopicPaths,
|
relatedTopicPaths,
|
||||||
slicedMessages
|
slicedMessages
|
||||||
);
|
);
|
||||||
memoryRuntime.recordMemory(result.memoryUnit(), topicPath, relatedTopicPaths, activationProfile);
|
memoryRuntime.recordMemory(result.getMemoryUnit(), topicPath, relatedTopicPaths, activationProfile);
|
||||||
}).onFailure(exp -> memoryRuntime.recordMemory(
|
}).onFailure(exp -> memoryRuntime.recordMemory(
|
||||||
result.memoryUnit(),
|
result.getMemoryUnit(),
|
||||||
null,
|
null,
|
||||||
List.of(),
|
List.of(),
|
||||||
defaultActivationProfile()
|
defaultActivationProfile()
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Message> sliceMessages(RollingResult result) {
|
|
||||||
int size = result.memoryUnit().getConversationMessages().size();
|
|
||||||
int start = Math.clamp(result.memorySlice().getStartIndex(), 0, size);
|
|
||||||
int end = Math.clamp(result.memorySlice().getEndIndex(), start, size);
|
|
||||||
if (start >= end) {
|
|
||||||
return List.of();
|
|
||||||
}
|
|
||||||
return result.memoryUnit().getConversationMessages().subList(start, end);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Message resolveTopicTaskMessage(RollingResult result, List<Message> slicedMessages) {
|
private Message resolveTopicTaskMessage(RollingResult result, List<Message> slicedMessages) {
|
||||||
return new TaskBlock() {
|
return new TaskBlock() {
|
||||||
@Override
|
@Override
|
||||||
protected void fillXml(@NotNull Document document, @NotNull Element root) {
|
protected void fillXml(@NotNull Document document, @NotNull Element root) {
|
||||||
appendTextElement(document, root, "current_topic_tree", memoryRuntime.getTopicTree());
|
appendTextElement(document, root, "current_topic_tree", memoryRuntime.getTopicTree());
|
||||||
appendTextElement(document, root, "slice_summary", result.summary());
|
appendTextElement(document, root, "slice_summary", result.getSummary());
|
||||||
appendRepeatedElements(document, root, "message", slicedMessages, (messageElement, message) -> {
|
appendRepeatedElements(document, root, "message", slicedMessages, (messageElement, message) -> {
|
||||||
messageElement.setAttribute("role", message.roleValue());
|
messageElement.setAttribute("role", message.roleValue());
|
||||||
messageElement.setTextContent(message.getContent());
|
messageElement.setTextContent(message.getContent());
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import org.junit.jupiter.api.BeforeAll;
|
|||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.io.TempDir;
|
import org.junit.jupiter.api.io.TempDir;
|
||||||
import work.slhaf.partner.core.memory.pojo.MemorySlice;
|
import work.slhaf.partner.core.memory.pojo.MemorySliceSnapshot;
|
||||||
import work.slhaf.partner.core.memory.pojo.MemoryUnit;
|
import work.slhaf.partner.core.memory.pojo.MemoryUnitSnapshot;
|
||||||
import work.slhaf.partner.framework.agent.model.pojo.Message;
|
import work.slhaf.partner.framework.agent.model.pojo.Message;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
@@ -32,7 +32,7 @@ class MemoryCoreTest {
|
|||||||
void shouldCreateFirstSliceFromChatMessages() {
|
void shouldCreateFirstSliceFromChatMessages() {
|
||||||
String sessionId = memoryCore.getMemorySessionId();
|
String sessionId = memoryCore.getMemorySessionId();
|
||||||
|
|
||||||
MemoryUnit updatedUnit = memoryCore.updateMemoryUnit(List.of(
|
MemoryUnitSnapshot updatedUnit = memoryCore.updateMemoryUnit(List.of(
|
||||||
new Message(Message.Character.USER, "m0"),
|
new Message(Message.Character.USER, "m0"),
|
||||||
new Message(Message.Character.USER, "m1"),
|
new Message(Message.Character.USER, "m1"),
|
||||||
new Message(Message.Character.USER, "m2")
|
new Message(Message.Character.USER, "m2")
|
||||||
@@ -43,7 +43,7 @@ class MemoryCoreTest {
|
|||||||
updatedUnit.getConversationMessages().stream().map(Message::getContent).toList());
|
updatedUnit.getConversationMessages().stream().map(Message::getContent).toList());
|
||||||
assertEquals(1, updatedUnit.getSlices().size());
|
assertEquals(1, updatedUnit.getSlices().size());
|
||||||
|
|
||||||
MemorySlice firstSlice = updatedUnit.getSlices().getFirst();
|
MemorySliceSnapshot firstSlice = updatedUnit.getSlices().getFirst();
|
||||||
assertNotNull(firstSlice.getId());
|
assertNotNull(firstSlice.getId());
|
||||||
assertEquals(0, firstSlice.getStartIndex());
|
assertEquals(0, firstSlice.getStartIndex());
|
||||||
assertEquals(3, firstSlice.getEndIndex());
|
assertEquals(3, firstSlice.getEndIndex());
|
||||||
@@ -60,7 +60,7 @@ class MemoryCoreTest {
|
|||||||
new Message(Message.Character.USER, "m0")
|
new Message(Message.Character.USER, "m0")
|
||||||
), "first-summary");
|
), "first-summary");
|
||||||
|
|
||||||
MemoryUnit updatedUnit = memoryCore.updateMemoryUnit(List.of(
|
MemoryUnitSnapshot updatedUnit = memoryCore.updateMemoryUnit(List.of(
|
||||||
new Message(Message.Character.ASSISTANT, "m1"),
|
new Message(Message.Character.ASSISTANT, "m1"),
|
||||||
new Message(Message.Character.USER, "m2")
|
new Message(Message.Character.USER, "m2")
|
||||||
), "second-summary");
|
), "second-summary");
|
||||||
@@ -70,14 +70,14 @@ class MemoryCoreTest {
|
|||||||
updatedUnit.getConversationMessages().stream().map(Message::getContent).toList());
|
updatedUnit.getConversationMessages().stream().map(Message::getContent).toList());
|
||||||
assertEquals(2, updatedUnit.getSlices().size());
|
assertEquals(2, updatedUnit.getSlices().size());
|
||||||
|
|
||||||
MemorySlice appendedSlice = updatedUnit.getSlices().getLast();
|
MemorySliceSnapshot appendedSlice = updatedUnit.getSlices().getLast();
|
||||||
assertNotNull(appendedSlice.getId());
|
assertNotNull(appendedSlice.getId());
|
||||||
assertEquals(1, appendedSlice.getStartIndex());
|
assertEquals(1, appendedSlice.getStartIndex());
|
||||||
assertEquals(3, appendedSlice.getEndIndex());
|
assertEquals(3, appendedSlice.getEndIndex());
|
||||||
assertEquals("second-summary", appendedSlice.getSummary());
|
assertEquals("second-summary", appendedSlice.getSummary());
|
||||||
assertTrue(appendedSlice.getTimestamp() > 0);
|
assertTrue(appendedSlice.getTimestamp() > 0);
|
||||||
|
|
||||||
MemorySlice loadedSlice = memoryCore.getMemorySlice(sessionId, appendedSlice.getId()).getOrThrow();
|
MemorySliceSnapshot loadedSlice = memoryCore.getMemorySlice(sessionId, appendedSlice.getId()).getOrThrow();
|
||||||
assertNotNull(loadedSlice);
|
assertNotNull(loadedSlice);
|
||||||
assertEquals(1, loadedSlice.getStartIndex());
|
assertEquals(1, loadedSlice.getStartIndex());
|
||||||
assertEquals(3, loadedSlice.getEndIndex());
|
assertEquals(3, loadedSlice.getEndIndex());
|
||||||
|
|||||||
+19
-16
@@ -6,7 +6,9 @@ import org.junit.jupiter.api.io.TempDir;
|
|||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
import work.slhaf.partner.core.memory.MemoryCapability;
|
import work.slhaf.partner.core.memory.MemoryCapability;
|
||||||
import work.slhaf.partner.core.memory.pojo.MemorySlice;
|
import work.slhaf.partner.core.memory.pojo.MemorySlice;
|
||||||
|
import work.slhaf.partner.core.memory.pojo.MemorySliceSnapshot;
|
||||||
import work.slhaf.partner.core.memory.pojo.MemoryUnit;
|
import work.slhaf.partner.core.memory.pojo.MemoryUnit;
|
||||||
|
import work.slhaf.partner.core.memory.pojo.MemoryUnitSnapshot;
|
||||||
import work.slhaf.partner.framework.agent.model.pojo.Message;
|
import work.slhaf.partner.framework.agent.model.pojo.Message;
|
||||||
import work.slhaf.partner.framework.agent.support.Result;
|
import work.slhaf.partner.framework.agent.support.Result;
|
||||||
import work.slhaf.partner.module.communication.summarizer.MessageCompressor;
|
import work.slhaf.partner.module.communication.summarizer.MessageCompressor;
|
||||||
@@ -63,19 +65,19 @@ class DialogRollingTest {
|
|||||||
message(Message.Character.ASSISTANT, "new-assistant")
|
message(Message.Character.ASSISTANT, "new-assistant")
|
||||||
), 4, 6);
|
), 4, 6);
|
||||||
|
|
||||||
MemoryUnit merged = memoryCapability.getMemoryUnit(sessionId);
|
MemoryUnitSnapshot merged = memoryCapability.getMemoryUnit(sessionId);
|
||||||
assertEquals(List.of("old-user", "old-assistant", "new-user", "new-assistant"),
|
assertEquals(List.of("old-user", "old-assistant", "new-user", "new-assistant"),
|
||||||
merged.getConversationMessages().stream().map(Message::getContent).toList());
|
merged.getConversationMessages().stream().map(Message::getContent).toList());
|
||||||
assertEquals(2, merged.getSlices().size());
|
assertEquals(2, merged.getSlices().size());
|
||||||
|
|
||||||
MemorySlice appendedSlice = merged.getSlices().getLast();
|
MemorySliceSnapshot appendedSlice = merged.getSlices().getLast();
|
||||||
assertNotNull(appendedSlice.getId());
|
assertNotNull(appendedSlice.getId());
|
||||||
assertEquals(2, appendedSlice.getStartIndex());
|
assertEquals(2, appendedSlice.getStartIndex());
|
||||||
assertEquals(4, appendedSlice.getEndIndex());
|
assertEquals(4, appendedSlice.getEndIndex());
|
||||||
assertEquals("new-summary", appendedSlice.getSummary());
|
assertEquals("new-summary", appendedSlice.getSummary());
|
||||||
assertEquals(sessionId, rollingResult.memoryUnit().getId());
|
assertEquals(sessionId, rollingResult.getMemoryUnit().getId());
|
||||||
assertEquals(appendedSlice.getId(), rollingResult.memorySlice().getId());
|
assertEquals(appendedSlice.getId(), rollingResult.getMemorySlice().getId());
|
||||||
assertEquals("new-summary", rollingResult.summary());
|
assertEquals("new-summary", rollingResult.getSummary());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -96,7 +98,7 @@ class DialogRollingTest {
|
|||||||
message(Message.Character.ASSISTANT, "second")
|
message(Message.Character.ASSISTANT, "second")
|
||||||
), 2, 6);
|
), 2, 6);
|
||||||
|
|
||||||
MemoryUnit created = memoryCapability.getMemoryUnit(sessionId);
|
MemoryUnitSnapshot created = memoryCapability.getMemoryUnit(sessionId);
|
||||||
assertNotNull(created);
|
assertNotNull(created);
|
||||||
assertEquals(List.of("first", "second"),
|
assertEquals(List.of("first", "second"),
|
||||||
created.getConversationMessages().stream().map(Message::getContent).toList());
|
created.getConversationMessages().stream().map(Message::getContent).toList());
|
||||||
@@ -104,7 +106,7 @@ class DialogRollingTest {
|
|||||||
assertEquals(0, created.getSlices().getFirst().getStartIndex());
|
assertEquals(0, created.getSlices().getFirst().getStartIndex());
|
||||||
assertEquals(2, created.getSlices().getFirst().getEndIndex());
|
assertEquals(2, created.getSlices().getFirst().getEndIndex());
|
||||||
assertEquals("fresh-summary", created.getSlices().getFirst().getSummary());
|
assertEquals("fresh-summary", created.getSlices().getFirst().getSummary());
|
||||||
assertEquals(created, rollingResult.memoryUnit());
|
assertEquals(created, rollingResult.getMemoryUnit());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -151,8 +153,8 @@ class DialogRollingTest {
|
|||||||
message(Message.Character.ASSISTANT, "a1")
|
message(Message.Character.ASSISTANT, "a1")
|
||||||
), 2, 6);
|
), 2, 6);
|
||||||
|
|
||||||
assertEquals(sessionId, rollingResult.memoryUnit().getId());
|
assertEquals(sessionId, rollingResult.getMemoryUnit().getId());
|
||||||
assertEquals("no summary, due to empty summarize result", rollingResult.summary());
|
assertEquals("no summary, due to empty summarize result", rollingResult.getSummary());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class StubMemoryCapability implements MemoryCapability {
|
private static final class StubMemoryCapability implements MemoryCapability {
|
||||||
@@ -172,28 +174,29 @@ class DialogRollingTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MemoryUnit getMemoryUnit(String unitId) {
|
public MemoryUnitSnapshot getMemoryUnit(String unitId) {
|
||||||
return units.get(unitId);
|
MemoryUnit unit = units.get(unitId);
|
||||||
|
return unit == null ? null : unit.snapshot();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public work.slhaf.partner.framework.agent.support.Result<MemorySlice> getMemorySlice(String unitId, String sliceId) {
|
public work.slhaf.partner.framework.agent.support.Result<MemorySliceSnapshot> getMemorySlice(String unitId, String sliceId) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MemoryUnit updateMemoryUnit(List<Message> chatMessages, String summary) {
|
public MemoryUnitSnapshot updateMemoryUnit(List<Message> chatMessages, String summary) {
|
||||||
MemoryUnit unit = units.computeIfAbsent(sessionId, MemoryUnit::new);
|
MemoryUnit unit = units.computeIfAbsent(sessionId, MemoryUnit::new);
|
||||||
unit.updateTimestamp();
|
unit.updateTimestamp();
|
||||||
int startIndex = unit.getConversationMessages().size();
|
int startIndex = unit.getConversationMessages().size();
|
||||||
unit.getConversationMessages().addAll(chatMessages);
|
unit.getConversationMessages().addAll(chatMessages);
|
||||||
unit.getSlices().add(new MemorySlice(startIndex, startIndex + chatMessages.size(), summary));
|
unit.getSlices().add(new MemorySlice(startIndex, startIndex + chatMessages.size(), summary));
|
||||||
return unit;
|
return unit.snapshot();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<MemoryUnit> listMemoryUnits() {
|
public Collection<MemoryUnitSnapshot> listMemoryUnits() {
|
||||||
return units.values();
|
return units.values().stream().map(MemoryUnit::snapshot).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+26
-27
@@ -11,7 +11,9 @@ import work.slhaf.partner.core.cognition.CognitionCapability;
|
|||||||
import work.slhaf.partner.core.cognition.context.ContextWorkspace;
|
import work.slhaf.partner.core.cognition.context.ContextWorkspace;
|
||||||
import work.slhaf.partner.core.memory.MemoryCapability;
|
import work.slhaf.partner.core.memory.MemoryCapability;
|
||||||
import work.slhaf.partner.core.memory.pojo.MemorySlice;
|
import work.slhaf.partner.core.memory.pojo.MemorySlice;
|
||||||
|
import work.slhaf.partner.core.memory.pojo.MemorySliceSnapshot;
|
||||||
import work.slhaf.partner.core.memory.pojo.MemoryUnit;
|
import work.slhaf.partner.core.memory.pojo.MemoryUnit;
|
||||||
|
import work.slhaf.partner.core.memory.pojo.MemoryUnitSnapshot;
|
||||||
import work.slhaf.partner.framework.agent.model.pojo.Message;
|
import work.slhaf.partner.framework.agent.model.pojo.Message;
|
||||||
import work.slhaf.partner.framework.agent.support.Result;
|
import work.slhaf.partner.framework.agent.support.Result;
|
||||||
import work.slhaf.partner.module.memory.pojo.ActivationProfile;
|
import work.slhaf.partner.module.memory.pojo.ActivationProfile;
|
||||||
@@ -19,7 +21,6 @@ import work.slhaf.partner.module.memory.runtime.exception.MemoryLookupException;
|
|||||||
import work.slhaf.partner.module.memory.selector.ActivatedMemorySlice;
|
import work.slhaf.partner.module.memory.selector.ActivatedMemorySlice;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@@ -41,11 +42,8 @@ class MemoryRuntimeTest {
|
|||||||
System.setProperty("user.home", tempDir.toAbsolutePath().toString());
|
System.setProperty("user.home", tempDir.toAbsolutePath().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
private static List<Message> invokeSliceMessages(MemoryRuntime runtime, MemoryUnit unit, MemorySlice slice) {
|
||||||
private static List<Message> invokeSliceMessages(MemoryRuntime runtime, MemoryUnit unit, MemorySlice slice) throws Exception {
|
return unit.snapshot().messagesOf(slice.snapshot());
|
||||||
Method method = MemoryRuntime.class.getDeclaredMethod("sliceMessages", MemoryUnit.class, MemorySlice.class);
|
|
||||||
method.setAccessible(true);
|
|
||||||
return (List<Message>) method.invoke(runtime, unit, slice);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void setField(Object target, String fieldName, Object value) throws Exception {
|
private static void setField(Object target, String fieldName, Object value) throws Exception {
|
||||||
@@ -200,7 +198,7 @@ class MemoryRuntimeTest {
|
|||||||
unit.getSlices().addAll(List.of(firstSlice, secondSlice));
|
unit.getSlices().addAll(List.of(firstSlice, secondSlice));
|
||||||
memoryCapability.remember(unit);
|
memoryCapability.remember(unit);
|
||||||
|
|
||||||
runtime.recordMemory(unit, "topic/main", List.of("topic/related"), DEFAULT_PROFILE);
|
runtime.recordMemory(unit.snapshot(), "topic/main", List.of("topic/related"), DEFAULT_PROFILE);
|
||||||
|
|
||||||
List<ActivatedMemorySlice> topicResult = runtime.queryActivatedMemoryByTopicPath("topic/main");
|
List<ActivatedMemorySlice> topicResult = runtime.queryActivatedMemoryByTopicPath("topic/main");
|
||||||
assertEquals(List.of("slice-2"), topicResult.stream().map(ActivatedMemorySlice::getSliceId).toList());
|
assertEquals(List.of("slice-2"), topicResult.stream().map(ActivatedMemorySlice::getSliceId).toList());
|
||||||
@@ -240,8 +238,8 @@ class MemoryRuntimeTest {
|
|||||||
relatedUnit.getSlices().add(relatedSlice);
|
relatedUnit.getSlices().add(relatedSlice);
|
||||||
memoryCapability.remember(relatedUnit);
|
memoryCapability.remember(relatedUnit);
|
||||||
|
|
||||||
runtime.recordMemory(mainUnit, "topic/main", List.of("topic/related"), DEFAULT_PROFILE);
|
runtime.recordMemory(mainUnit.snapshot(), "topic/main", List.of("topic/related"), DEFAULT_PROFILE);
|
||||||
runtime.recordMemory(relatedUnit, "topic/related", List.of(), DEFAULT_PROFILE);
|
runtime.recordMemory(relatedUnit.snapshot(), "topic/related", List.of(), DEFAULT_PROFILE);
|
||||||
|
|
||||||
List<ActivatedMemorySlice> topicResult = runtime.queryActivatedMemoryByTopicPath("topic/main");
|
List<ActivatedMemorySlice> topicResult = runtime.queryActivatedMemoryByTopicPath("topic/main");
|
||||||
assertEquals(List.of("slice-main", "slice-related"),
|
assertEquals(List.of("slice-main", "slice-related"),
|
||||||
@@ -260,7 +258,7 @@ class MemoryRuntimeTest {
|
|||||||
MemorySlice firstSlice = MemorySlice.restore("slice-1", 0, 1, "first", 86_400_000L);
|
MemorySlice firstSlice = MemorySlice.restore("slice-1", 0, 1, "first", 86_400_000L);
|
||||||
firstUnitSnapshot.getSlices().add(firstSlice);
|
firstUnitSnapshot.getSlices().add(firstSlice);
|
||||||
memoryCapability.remember(firstUnitSnapshot);
|
memoryCapability.remember(firstUnitSnapshot);
|
||||||
runtime.recordMemory(firstUnitSnapshot, "topic/main", List.of(), DEFAULT_PROFILE);
|
runtime.recordMemory(firstUnitSnapshot.snapshot(), "topic/main", List.of(), DEFAULT_PROFILE);
|
||||||
|
|
||||||
firstUnitSnapshot.getConversationMessages().clear();
|
firstUnitSnapshot.getConversationMessages().clear();
|
||||||
firstUnitSnapshot.getConversationMessages().addAll(List.of(message("m2"), message("m3")));
|
firstUnitSnapshot.getConversationMessages().addAll(List.of(message("m2"), message("m3")));
|
||||||
@@ -268,7 +266,7 @@ class MemoryRuntimeTest {
|
|||||||
firstUnitSnapshot.getSlices().clear();
|
firstUnitSnapshot.getSlices().clear();
|
||||||
firstUnitSnapshot.getSlices().add(secondSlice);
|
firstUnitSnapshot.getSlices().add(secondSlice);
|
||||||
memoryCapability.remember(firstUnitSnapshot);
|
memoryCapability.remember(firstUnitSnapshot);
|
||||||
runtime.recordMemory(firstUnitSnapshot, "topic/main", List.of(), DEFAULT_PROFILE);
|
runtime.recordMemory(firstUnitSnapshot.snapshot(), "topic/main", List.of(), DEFAULT_PROFILE);
|
||||||
|
|
||||||
JSONObject state = JSONObject.parseObject(runtime.convert().toString());
|
JSONObject state = JSONObject.parseObject(runtime.convert().toString());
|
||||||
JSONArray dateIndex = state.getJSONArray("date_index");
|
JSONArray dateIndex = state.getJSONArray("date_index");
|
||||||
@@ -306,14 +304,14 @@ class MemoryRuntimeTest {
|
|||||||
MemorySlice secondSlice = MemorySlice.restore("slice-2", 2, 4, "second", 172_800_000L);
|
MemorySlice secondSlice = MemorySlice.restore("slice-2", 2, 4, "second", 172_800_000L);
|
||||||
mainUnit.getSlices().addAll(List.of(firstSlice, secondSlice));
|
mainUnit.getSlices().addAll(List.of(firstSlice, secondSlice));
|
||||||
memoryCapability.remember(mainUnit);
|
memoryCapability.remember(mainUnit);
|
||||||
runtime.recordMemory(mainUnit, "topic/main", List.of("topic/related"), DEFAULT_PROFILE);
|
runtime.recordMemory(mainUnit.snapshot(), "topic/main", List.of("topic/related"), DEFAULT_PROFILE);
|
||||||
|
|
||||||
MemoryUnit relatedUnit = new MemoryUnit("unit-201");
|
MemoryUnit relatedUnit = new MemoryUnit("unit-201");
|
||||||
relatedUnit.getConversationMessages().addAll(List.of(message("r0"), message("r1")));
|
relatedUnit.getConversationMessages().addAll(List.of(message("r0"), message("r1")));
|
||||||
MemorySlice relatedSlice = MemorySlice.restore("slice-3", 0, 2, "related", 259_200_000L);
|
MemorySlice relatedSlice = MemorySlice.restore("slice-3", 0, 2, "related", 259_200_000L);
|
||||||
relatedUnit.getSlices().add(relatedSlice);
|
relatedUnit.getSlices().add(relatedSlice);
|
||||||
memoryCapability.remember(relatedUnit);
|
memoryCapability.remember(relatedUnit);
|
||||||
runtime.recordMemory(relatedUnit, "topic/related", List.of(), DEFAULT_PROFILE);
|
runtime.recordMemory(relatedUnit.snapshot(), "topic/related", List.of(), DEFAULT_PROFILE);
|
||||||
|
|
||||||
JSONObject state = JSONObject.parseObject(runtime.convert().toString());
|
JSONObject state = JSONObject.parseObject(runtime.convert().toString());
|
||||||
JSONArray topicSlices = state.getJSONArray("topic_slices");
|
JSONArray topicSlices = state.getJSONArray("topic_slices");
|
||||||
@@ -380,21 +378,21 @@ class MemoryRuntimeTest {
|
|||||||
MemorySlice primarySlice = MemorySlice.restore("slice-primary", 0, 2, "primary", System.currentTimeMillis());
|
MemorySlice primarySlice = MemorySlice.restore("slice-primary", 0, 2, "primary", System.currentTimeMillis());
|
||||||
primaryUnit.getSlices().add(primarySlice);
|
primaryUnit.getSlices().add(primarySlice);
|
||||||
memoryCapability.remember(primaryUnit);
|
memoryCapability.remember(primaryUnit);
|
||||||
runtime.recordMemory(primaryUnit, "topic->main", List.of("topic->related"), new ActivationProfile(0.9f, 0.1f, 0.9f));
|
runtime.recordMemory(primaryUnit.snapshot(), "topic->main", List.of("topic->related"), new ActivationProfile(0.9f, 0.1f, 0.9f));
|
||||||
|
|
||||||
MemoryUnit relatedUnit = new MemoryUnit("unit-related-rank");
|
MemoryUnit relatedUnit = new MemoryUnit("unit-related-rank");
|
||||||
relatedUnit.getConversationMessages().addAll(List.of(message("r0"), message("r1")));
|
relatedUnit.getConversationMessages().addAll(List.of(message("r0"), message("r1")));
|
||||||
MemorySlice relatedSlice = MemorySlice.restore("slice-related-rank", 0, 2, "related", System.currentTimeMillis());
|
MemorySlice relatedSlice = MemorySlice.restore("slice-related-rank", 0, 2, "related", System.currentTimeMillis());
|
||||||
relatedUnit.getSlices().add(relatedSlice);
|
relatedUnit.getSlices().add(relatedSlice);
|
||||||
memoryCapability.remember(relatedUnit);
|
memoryCapability.remember(relatedUnit);
|
||||||
runtime.recordMemory(relatedUnit, "topic->related", List.of(), new ActivationProfile(1.0f, 1.0f, 1.0f));
|
runtime.recordMemory(relatedUnit.snapshot(), "topic->related", List.of(), new ActivationProfile(1.0f, 1.0f, 1.0f));
|
||||||
|
|
||||||
MemoryUnit parentUnit = new MemoryUnit("unit-parent");
|
MemoryUnit parentUnit = new MemoryUnit("unit-parent");
|
||||||
parentUnit.getConversationMessages().addAll(List.of(message("x0"), message("x1")));
|
parentUnit.getConversationMessages().addAll(List.of(message("x0"), message("x1")));
|
||||||
MemorySlice parentSlice = MemorySlice.restore("slice-parent", 0, 2, "parent", System.currentTimeMillis());
|
MemorySlice parentSlice = MemorySlice.restore("slice-parent", 0, 2, "parent", System.currentTimeMillis());
|
||||||
parentUnit.getSlices().add(parentSlice);
|
parentUnit.getSlices().add(parentSlice);
|
||||||
memoryCapability.remember(parentUnit);
|
memoryCapability.remember(parentUnit);
|
||||||
runtime.recordMemory(parentUnit, "topic", List.of(), new ActivationProfile(1.0f, 1.0f, 1.0f));
|
runtime.recordMemory(parentUnit.snapshot(), "topic", List.of(), new ActivationProfile(1.0f, 1.0f, 1.0f));
|
||||||
|
|
||||||
List<ActivatedMemorySlice> topicResult = runtime.queryActivatedMemoryByTopicPath("topic->main");
|
List<ActivatedMemorySlice> topicResult = runtime.queryActivatedMemoryByTopicPath("topic->main");
|
||||||
assertEquals(List.of("slice-primary", "slice-related-rank", "slice-parent"),
|
assertEquals(List.of("slice-primary", "slice-related-rank", "slice-parent"),
|
||||||
@@ -414,7 +412,7 @@ class MemoryRuntimeTest {
|
|||||||
primaryUnit.getSlices().add(primarySlice);
|
primaryUnit.getSlices().add(primarySlice);
|
||||||
memoryCapability.remember(primaryUnit);
|
memoryCapability.remember(primaryUnit);
|
||||||
runtime.recordMemory(
|
runtime.recordMemory(
|
||||||
primaryUnit,
|
primaryUnit.snapshot(),
|
||||||
"topic->main",
|
"topic->main",
|
||||||
List.of("topic->related"),
|
List.of("topic->related"),
|
||||||
new ActivationProfile(0.8f, 0.0f, 0.8f)
|
new ActivationProfile(0.8f, 0.0f, 0.8f)
|
||||||
@@ -425,7 +423,7 @@ class MemoryRuntimeTest {
|
|||||||
MemorySlice relatedSlice = MemorySlice.restore("slice-related-zero", 0, 2, "related", System.currentTimeMillis());
|
MemorySlice relatedSlice = MemorySlice.restore("slice-related-zero", 0, 2, "related", System.currentTimeMillis());
|
||||||
relatedUnit.getSlices().add(relatedSlice);
|
relatedUnit.getSlices().add(relatedSlice);
|
||||||
memoryCapability.remember(relatedUnit);
|
memoryCapability.remember(relatedUnit);
|
||||||
runtime.recordMemory(relatedUnit, "topic->related", List.of(), new ActivationProfile(1.0f, 1.0f, 1.0f));
|
runtime.recordMemory(relatedUnit.snapshot(), "topic->related", List.of(), new ActivationProfile(1.0f, 1.0f, 1.0f));
|
||||||
|
|
||||||
List<ActivatedMemorySlice> topicResult = runtime.queryActivatedMemoryByTopicPath("topic->main");
|
List<ActivatedMemorySlice> topicResult = runtime.queryActivatedMemoryByTopicPath("topic->main");
|
||||||
assertEquals(List.of("slice-primary-zero"), topicResult.stream().map(ActivatedMemorySlice::getSliceId).toList());
|
assertEquals(List.of("slice-primary-zero"), topicResult.stream().map(ActivatedMemorySlice::getSliceId).toList());
|
||||||
@@ -444,10 +442,10 @@ class MemoryRuntimeTest {
|
|||||||
unit.getSlices().add(slice);
|
unit.getSlices().add(slice);
|
||||||
memoryCapability.remember(unit);
|
memoryCapability.remember(unit);
|
||||||
|
|
||||||
runtime.recordMemory(unit, "topic->main", List.of("topic->related"), new ActivationProfile(0.2f, 0.1f, 0.2f));
|
runtime.recordMemory(unit.snapshot(), "topic->main", List.of("topic->related"), new ActivationProfile(0.2f, 0.1f, 0.2f));
|
||||||
unit.getSlices().clear();
|
unit.getSlices().clear();
|
||||||
unit.getSlices().add(MemorySlice.restore("slice-refresh", 0, 2, "summary", 172_800_000L));
|
unit.getSlices().add(MemorySlice.restore("slice-refresh", 0, 2, "summary", 172_800_000L));
|
||||||
runtime.recordMemory(unit, "topic->main", List.of("topic->related-2"), new ActivationProfile(0.9f, 0.8f, 0.7f));
|
runtime.recordMemory(unit.snapshot(), "topic->main", List.of("topic->related-2"), new ActivationProfile(0.9f, 0.8f, 0.7f));
|
||||||
|
|
||||||
JSONObject state = JSONObject.parseObject(runtime.convert().toString());
|
JSONObject state = JSONObject.parseObject(runtime.convert().toString());
|
||||||
JSONObject mainTopic = state.getJSONArray("topic_slices").stream()
|
JSONObject mainTopic = state.getJSONArray("topic_slices").stream()
|
||||||
@@ -481,12 +479,13 @@ class MemoryRuntimeTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MemoryUnit getMemoryUnit(String unitId) {
|
public MemoryUnitSnapshot getMemoryUnit(String unitId) {
|
||||||
return units.get(unitId);
|
MemoryUnit unit = units.get(unitId);
|
||||||
|
return unit == null ? null : unit.snapshot();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Result<MemorySlice> getMemorySlice(String unitId, String sliceId) {
|
public Result<MemorySliceSnapshot> getMemorySlice(String unitId, String sliceId) {
|
||||||
MemoryUnit unit = units.get(unitId);
|
MemoryUnit unit = units.get(unitId);
|
||||||
if (unit == null || unit.getSlices() == null) {
|
if (unit == null || unit.getSlices() == null) {
|
||||||
return Result.failure(new MemoryLookupException(
|
return Result.failure(new MemoryLookupException(
|
||||||
@@ -498,7 +497,7 @@ class MemoryRuntimeTest {
|
|||||||
return unit.getSlices().stream()
|
return unit.getSlices().stream()
|
||||||
.filter(slice -> sliceId.equals(slice.getId()))
|
.filter(slice -> sliceId.equals(slice.getId()))
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.map(Result::success)
|
.map(slice -> Result.success(slice.snapshot()))
|
||||||
.orElseGet(() -> Result.failure(new MemoryLookupException(
|
.orElseGet(() -> Result.failure(new MemoryLookupException(
|
||||||
"Memory slice not found: " + unitId + ":" + sliceId,
|
"Memory slice not found: " + unitId + ":" + sliceId,
|
||||||
unitId + ":" + sliceId,
|
unitId + ":" + sliceId,
|
||||||
@@ -507,13 +506,13 @@ class MemoryRuntimeTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MemoryUnit updateMemoryUnit(List<Message> chatMessages, String summary) {
|
public MemoryUnitSnapshot updateMemoryUnit(List<Message> chatMessages, String summary) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<MemoryUnit> listMemoryUnits() {
|
public Collection<MemoryUnitSnapshot> listMemoryUnits() {
|
||||||
return units.values();
|
return units.values().stream().map(MemoryUnit::snapshot).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+6
-9
@@ -79,13 +79,10 @@ class MemoryRecallProfileExtractorTest {
|
|||||||
MemorySlice slice = new MemorySlice(2, 4, "slice-summary");
|
MemorySlice slice = new MemorySlice(2, 4, "slice-summary");
|
||||||
unit.getSlices().add(slice);
|
unit.getSlices().add(slice);
|
||||||
|
|
||||||
updater.consume(new RollingResult(unit, slice, List.of(
|
updater.consume(new RollingResult(unit.snapshot(), slice.snapshot(), 4, 6));
|
||||||
message(Message.Character.USER, "new"),
|
|
||||||
message(Message.Character.ASSISTANT, "new-reply")
|
|
||||||
), "slice-summary", 4, 6));
|
|
||||||
|
|
||||||
verify(memoryRuntime).recordMemory(
|
verify(memoryRuntime).recordMemory(
|
||||||
eq(unit),
|
eq(unit.snapshot()),
|
||||||
eq("root->branch"),
|
eq("root->branch"),
|
||||||
eq(List.of("root->related")),
|
eq(List.of("root->related")),
|
||||||
argThat(profile -> profile != null
|
argThat(profile -> profile != null
|
||||||
@@ -113,10 +110,10 @@ class MemoryRecallProfileExtractorTest {
|
|||||||
MemorySlice slice = new MemorySlice(0, 2, "slice-summary");
|
MemorySlice slice = new MemorySlice(0, 2, "slice-summary");
|
||||||
unit.getSlices().add(slice);
|
unit.getSlices().add(slice);
|
||||||
|
|
||||||
updater.consume(new RollingResult(unit, slice, unit.getConversationMessages(), "slice-summary", 2, 6));
|
updater.consume(new RollingResult(unit.snapshot(), slice.snapshot(), 2, 6));
|
||||||
|
|
||||||
verify(memoryRuntime).recordMemory(
|
verify(memoryRuntime).recordMemory(
|
||||||
eq(unit),
|
eq(unit.snapshot()),
|
||||||
eq(null),
|
eq(null),
|
||||||
eq(List.of()),
|
eq(List.of()),
|
||||||
argThat(profile -> profile != null
|
argThat(profile -> profile != null
|
||||||
@@ -147,10 +144,10 @@ class MemoryRecallProfileExtractorTest {
|
|||||||
MemorySlice slice = new MemorySlice(0, 1, "slice-summary");
|
MemorySlice slice = new MemorySlice(0, 1, "slice-summary");
|
||||||
unit.getSlices().add(slice);
|
unit.getSlices().add(slice);
|
||||||
|
|
||||||
updater.consume(new RollingResult(unit, slice, unit.getConversationMessages(), "slice-summary", 1, 6));
|
updater.consume(new RollingResult(unit.snapshot(), slice.snapshot(), 1, 6));
|
||||||
|
|
||||||
verify(memoryRuntime).recordMemory(
|
verify(memoryRuntime).recordMemory(
|
||||||
eq(unit),
|
eq(unit.snapshot()),
|
||||||
eq("root->branch"),
|
eq("root->branch"),
|
||||||
eq(List.of()),
|
eq(List.of()),
|
||||||
argThat(profile -> profile != null
|
argThat(profile -> profile != null
|
||||||
|
|||||||
Reference in New Issue
Block a user