mirror of
https://github.com/Youzini-afk/ST-Bionic-Memory-Ecology.git
synced 2026-05-15 22:30:38 +08:00
fix: harden hydrate normalized fast-path and vector scope guards
This commit is contained in:
@@ -1,4 +1,9 @@
|
|||||||
import { createEmptyGraph, deserializeGraph } from "../graph/graph.js";
|
import { createEmptyGraph, deserializeGraph } from "../graph/graph.js";
|
||||||
|
import { normalizeMemoryScope } from "../graph/memory-scope.js";
|
||||||
|
import {
|
||||||
|
normalizeStoryTime,
|
||||||
|
normalizeStoryTimeSpan,
|
||||||
|
} from "../graph/story-timeline.js";
|
||||||
import {
|
import {
|
||||||
buildVectorCollectionId,
|
buildVectorCollectionId,
|
||||||
cloneGraphPersistDirtyState,
|
cloneGraphPersistDirtyState,
|
||||||
@@ -508,6 +513,49 @@ function cloneHydrateSnapshotEdgeRecords(records = []) {
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isNormalizedSnapshotNodeRecord(record = null) {
|
||||||
|
if (!record || typeof record !== "object" || Array.isArray(record)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!Array.isArray(record.seqRange) || record.seqRange.length < 2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!Array.isArray(record.childIds) || !Array.isArray(record.clusters)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (normalizeMemoryScope(record.scope) !== record.scope) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (normalizeStoryTime(record.storyTime) !== record.storyTime) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (normalizeStoryTimeSpan(record.storyTimeSpan) !== record.storyTimeSpan) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isNormalizedSnapshotEdgeRecord(record = null) {
|
||||||
|
if (!record || typeof record !== "object" || Array.isArray(record)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return normalizeMemoryScope(record.scope) === record.scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
function areSnapshotRecordsNormalized(snapshotView = {}) {
|
||||||
|
for (const node of toArray(snapshotView?.nodes)) {
|
||||||
|
if (!isNormalizedSnapshotNodeRecord(node)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const edge of toArray(snapshotView?.edges)) {
|
||||||
|
if (!isNormalizedSnapshotEdgeRecord(edge)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
function toMetaMap(rows = []) {
|
function toMetaMap(rows = []) {
|
||||||
const output = {};
|
const output = {};
|
||||||
for (const row of rows) {
|
for (const row of rows) {
|
||||||
@@ -2906,7 +2954,8 @@ export function buildGraphFromSnapshot(snapshot, options = {}) {
|
|||||||
{},
|
{},
|
||||||
);
|
);
|
||||||
const snapshotRecordsNormalized =
|
const snapshotRecordsNormalized =
|
||||||
snapshotMeta?.[BME_RUNTIME_RECORDS_NORMALIZED_META_KEY] === true;
|
snapshotMeta?.[BME_RUNTIME_RECORDS_NORMALIZED_META_KEY] === true &&
|
||||||
|
areSnapshotRecordsNormalized(snapshotView);
|
||||||
const nativeHydrateGate =
|
const nativeHydrateGate =
|
||||||
options?.useNativeHydrate === true
|
options?.useNativeHydrate === true
|
||||||
? evaluateNativeHydrateGate(snapshotView, options)
|
? evaluateNativeHydrateGate(snapshotView, options)
|
||||||
|
|||||||
@@ -734,6 +734,16 @@ async function testGraphSnapshotConverters() {
|
|||||||
const rebuiltLegacyCompatible = buildGraphFromSnapshot(legacyCompatibleSnapshot, {
|
const rebuiltLegacyCompatible = buildGraphFromSnapshot(legacyCompatibleSnapshot, {
|
||||||
chatId: "chat-a",
|
chatId: "chat-a",
|
||||||
});
|
});
|
||||||
|
const malformedButFlaggedSnapshot = {
|
||||||
|
...legacyCompatibleSnapshot,
|
||||||
|
meta: {
|
||||||
|
...legacyCompatibleSnapshot.meta,
|
||||||
|
[BME_RUNTIME_RECORDS_NORMALIZED_META_KEY]: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const rebuiltMalformedButFlagged = buildGraphFromSnapshot(malformedButFlaggedSnapshot, {
|
||||||
|
chatId: "chat-a",
|
||||||
|
});
|
||||||
assert.equal(rebuilt.historyState.lastProcessedAssistantFloor, 9);
|
assert.equal(rebuilt.historyState.lastProcessedAssistantFloor, 9);
|
||||||
assert.equal(rebuilt.historyState.extractionCount, 4);
|
assert.equal(rebuilt.historyState.extractionCount, 4);
|
||||||
assert.equal(rebuilt.nodes.length, 1);
|
assert.equal(rebuilt.nodes.length, 1);
|
||||||
@@ -751,6 +761,9 @@ async function testGraphSnapshotConverters() {
|
|||||||
assert.equal(rebuiltLegacyCompatible.nodes[0].scope?.layer, "objective");
|
assert.equal(rebuiltLegacyCompatible.nodes[0].scope?.layer, "objective");
|
||||||
assert.equal(rebuiltLegacyCompatible.nodes[0].storyTime?.tense, "unknown");
|
assert.equal(rebuiltLegacyCompatible.nodes[0].storyTime?.tense, "unknown");
|
||||||
assert.equal(rebuiltLegacyCompatible.nodes[0].storyTimeSpan?.mixed, false);
|
assert.equal(rebuiltLegacyCompatible.nodes[0].storyTimeSpan?.mixed, false);
|
||||||
|
assert.equal(rebuiltMalformedButFlagged.nodes[0].scope?.layer, "objective");
|
||||||
|
assert.equal(rebuiltMalformedButFlagged.nodes[0].storyTime?.tense, "unknown");
|
||||||
|
assert.equal(rebuiltMalformedButFlagged.nodes[0].storyTimeSpan?.mixed, false);
|
||||||
|
|
||||||
rebuilt.nodes[0].fields.title = "Mutated Converter Node";
|
rebuilt.nodes[0].fields.title = "Mutated Converter Node";
|
||||||
rebuilt.nodes[0].embedding[0] = 99;
|
rebuilt.nodes[0].embedding[0] = 99;
|
||||||
|
|||||||
@@ -302,14 +302,18 @@ export function buildNodeVectorText(node) {
|
|||||||
|
|
||||||
const scope = normalizeMemoryScope(node?.scope);
|
const scope = normalizeMemoryScope(node?.scope);
|
||||||
const scopeText = describeMemoryScope(scope);
|
const scopeText = describeMemoryScope(scope);
|
||||||
|
const regionPath = Array.isArray(scope?.regionPath) ? scope.regionPath : [];
|
||||||
|
const regionSecondary = Array.isArray(scope?.regionSecondary)
|
||||||
|
? scope.regionSecondary
|
||||||
|
: [];
|
||||||
if (scopeText) {
|
if (scopeText) {
|
||||||
parts.push(`memory_scope: ${scopeText}`);
|
parts.push(`memory_scope: ${scopeText}`);
|
||||||
}
|
}
|
||||||
if (scope.regionPath.length > 0) {
|
if (regionPath.length > 0) {
|
||||||
parts.push(`memory_region_path: ${scope.regionPath.join(" / ")}`);
|
parts.push(`memory_region_path: ${regionPath.join(" / ")}`);
|
||||||
}
|
}
|
||||||
if (scope.regionSecondary.length > 0) {
|
if (regionSecondary.length > 0) {
|
||||||
parts.push(`memory_region_secondary: ${scope.regionSecondary.join(", ")}`);
|
parts.push(`memory_region_secondary: ${regionSecondary.join(", ")}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return parts.join(" | ").trim();
|
return parts.join(" | ").trim();
|
||||||
|
|||||||
Reference in New Issue
Block a user