mirror of
https://github.com/Youzini-afk/ST-Bionic-Memory-Ecology.git
synced 2026-05-15 22:30:38 +08:00
feat: support multi-owner scene recall anchors
This commit is contained in:
@@ -847,7 +847,11 @@ function listToSet(values = []) {
|
||||
return new Set(uniqueIds(values));
|
||||
}
|
||||
|
||||
export function computeKnowledgeGateForNode(
|
||||
function normalizeOwnerKeyList(ownerKeys = []) {
|
||||
return uniqueIds(Array.isArray(ownerKeys) ? ownerKeys : [ownerKeys]);
|
||||
}
|
||||
|
||||
function computeKnowledgeGateForSingleOwner(
|
||||
graph,
|
||||
node,
|
||||
ownerKey = "",
|
||||
@@ -971,6 +975,115 @@ export function computeKnowledgeGateForNode(
|
||||
};
|
||||
}
|
||||
|
||||
export function computeKnowledgeGateForNode(
|
||||
graph,
|
||||
node,
|
||||
ownerKey = "",
|
||||
{
|
||||
vectorScore = 0,
|
||||
graphScore = 0,
|
||||
lexicalScore = 0,
|
||||
scopeBucket = "",
|
||||
injectLowConfidenceObjectiveMemory = false,
|
||||
} = {},
|
||||
) {
|
||||
const normalizedOwnerKeys = normalizeOwnerKeyList(ownerKey);
|
||||
if (normalizedOwnerKeys.length <= 1) {
|
||||
const singleGate = computeKnowledgeGateForSingleOwner(
|
||||
graph,
|
||||
node,
|
||||
normalizedOwnerKeys[0] || "",
|
||||
{
|
||||
vectorScore,
|
||||
graphScore,
|
||||
lexicalScore,
|
||||
scopeBucket,
|
||||
injectLowConfidenceObjectiveMemory,
|
||||
},
|
||||
);
|
||||
return {
|
||||
...singleGate,
|
||||
ownerCoverage: singleGate.visible ? 1 : 0,
|
||||
visibleOwnerKeys: singleGate.visible && normalizedOwnerKeys[0]
|
||||
? [normalizedOwnerKeys[0]]
|
||||
: [],
|
||||
suppressedOwnerKeys:
|
||||
singleGate.visible || !normalizedOwnerKeys[0]
|
||||
? []
|
||||
: [normalizedOwnerKeys[0]],
|
||||
ownerResults: normalizedOwnerKeys[0]
|
||||
? { [normalizedOwnerKeys[0]]: singleGate }
|
||||
: {},
|
||||
};
|
||||
}
|
||||
|
||||
const ownerResults = {};
|
||||
const visibleOwnerKeys = [];
|
||||
const suppressedOwnerKeys = [];
|
||||
let bestVisibilityScore = 0;
|
||||
let bestThreshold = 0;
|
||||
let anchored = false;
|
||||
let rescued = false;
|
||||
let bestMode = "suppressed";
|
||||
let bestSuppressedReason = "";
|
||||
|
||||
for (const candidateOwnerKey of normalizedOwnerKeys) {
|
||||
const result = computeKnowledgeGateForSingleOwner(
|
||||
graph,
|
||||
node,
|
||||
candidateOwnerKey,
|
||||
{
|
||||
vectorScore,
|
||||
graphScore,
|
||||
lexicalScore,
|
||||
scopeBucket,
|
||||
injectLowConfidenceObjectiveMemory,
|
||||
},
|
||||
);
|
||||
ownerResults[candidateOwnerKey] = result;
|
||||
bestVisibilityScore = Math.max(
|
||||
bestVisibilityScore,
|
||||
Number(result.visibilityScore || 0),
|
||||
);
|
||||
bestThreshold = Math.max(bestThreshold, Number(result.threshold || 0));
|
||||
if (result.visible) {
|
||||
visibleOwnerKeys.push(candidateOwnerKey);
|
||||
anchored ||= Boolean(result.anchored);
|
||||
rescued ||= Boolean(result.rescued);
|
||||
if (
|
||||
bestMode === "suppressed" ||
|
||||
(result.anchored && bestMode !== "manual-known") ||
|
||||
(result.mode === "manual-known")
|
||||
) {
|
||||
bestMode = String(result.mode || bestMode);
|
||||
}
|
||||
} else {
|
||||
suppressedOwnerKeys.push(candidateOwnerKey);
|
||||
if (!bestSuppressedReason && result.suppressedReason) {
|
||||
bestSuppressedReason = String(result.suppressedReason || "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const visible = visibleOwnerKeys.length > 0;
|
||||
return {
|
||||
visible,
|
||||
anchored,
|
||||
rescued,
|
||||
suppressed: !visible,
|
||||
suppressedReason: visible ? "" : bestSuppressedReason || "low-visibility",
|
||||
visibilityScore: bestVisibilityScore,
|
||||
mode: visible ? bestMode : "suppressed",
|
||||
threshold: bestThreshold,
|
||||
ownerCoverage: normalizedOwnerKeys.length
|
||||
? visibleOwnerKeys.length / normalizedOwnerKeys.length
|
||||
: 0,
|
||||
visibleOwnerKeys,
|
||||
suppressedOwnerKeys,
|
||||
ownerResults,
|
||||
};
|
||||
}
|
||||
|
||||
export function applyManualKnowledgeOverride(
|
||||
graph,
|
||||
{ ownerKey = "", ownerType = "", ownerName = "", nodeId = "", mode = "known" } = {},
|
||||
|
||||
@@ -58,6 +58,12 @@ function normalizeStringArray(values = []) {
|
||||
return result;
|
||||
}
|
||||
|
||||
function normalizeOwnerValueSet(values = []) {
|
||||
return new Set(
|
||||
normalizeStringArray(values).map((value) => normalizeKey(value)),
|
||||
);
|
||||
}
|
||||
|
||||
function normalizeOwnerType(layer, ownerType) {
|
||||
if (layer !== MEMORY_SCOPE_LAYER.POV) {
|
||||
return MEMORY_SCOPE_OWNER_TYPE.NONE;
|
||||
@@ -224,11 +230,14 @@ export function classifyNodeScopeBucket(
|
||||
node,
|
||||
{
|
||||
activeCharacterPovOwner = "",
|
||||
activeCharacterPovOwners = [],
|
||||
activeUserPovOwner = "",
|
||||
activeUserPovOwners = [],
|
||||
activeRegion = "",
|
||||
adjacentRegions = [],
|
||||
enablePovMemory = true,
|
||||
enableRegionScopedObjective = true,
|
||||
allowImplicitCharacterPovFallback = true,
|
||||
} = {},
|
||||
) {
|
||||
const scope = normalizeMemoryScope(node?.scope);
|
||||
@@ -236,6 +245,18 @@ export function classifyNodeScopeBucket(
|
||||
const normalizedAdjacentRegions = new Set(
|
||||
normalizeStringArray(adjacentRegions).map((value) => normalizeKey(value)),
|
||||
);
|
||||
const normalizedActiveCharacterOwners = normalizeOwnerValueSet([
|
||||
...normalizeStringArray(activeCharacterPovOwners),
|
||||
activeCharacterPovOwner,
|
||||
]);
|
||||
const normalizedActiveUserOwners = normalizeOwnerValueSet([
|
||||
...normalizeStringArray(activeUserPovOwners),
|
||||
activeUserPovOwner,
|
||||
]);
|
||||
const scopeOwnerValues = normalizeOwnerValueSet([
|
||||
scope.ownerId,
|
||||
scope.ownerName,
|
||||
]);
|
||||
|
||||
if (scope.layer === MEMORY_SCOPE_LAYER.POV) {
|
||||
if (!enablePovMemory) {
|
||||
@@ -243,24 +264,29 @@ export function classifyNodeScopeBucket(
|
||||
}
|
||||
if (
|
||||
scope.ownerType === MEMORY_SCOPE_OWNER_TYPE.CHARACTER &&
|
||||
matchesScopeOwner(scope, MEMORY_SCOPE_OWNER_TYPE.CHARACTER, activeCharacterPovOwner)
|
||||
scopeOwnerValues.size > 0 &&
|
||||
[...scopeOwnerValues].some((value) =>
|
||||
normalizedActiveCharacterOwners.has(value),
|
||||
)
|
||||
) {
|
||||
return MEMORY_SCOPE_BUCKETS.CHARACTER_POV;
|
||||
}
|
||||
if (
|
||||
scope.ownerType === MEMORY_SCOPE_OWNER_TYPE.USER &&
|
||||
matchesScopeOwner(scope, MEMORY_SCOPE_OWNER_TYPE.USER, activeUserPovOwner)
|
||||
scopeOwnerValues.size > 0 &&
|
||||
[...scopeOwnerValues].some((value) => normalizedActiveUserOwners.has(value))
|
||||
) {
|
||||
return MEMORY_SCOPE_BUCKETS.USER_POV;
|
||||
}
|
||||
if (
|
||||
!normalizeString(activeCharacterPovOwner) &&
|
||||
allowImplicitCharacterPovFallback &&
|
||||
normalizedActiveCharacterOwners.size === 0 &&
|
||||
scope.ownerType === MEMORY_SCOPE_OWNER_TYPE.CHARACTER
|
||||
) {
|
||||
return MEMORY_SCOPE_BUCKETS.CHARACTER_POV;
|
||||
}
|
||||
if (
|
||||
!normalizeString(activeUserPovOwner) &&
|
||||
normalizedActiveUserOwners.size === 0 &&
|
||||
scope.ownerType === MEMORY_SCOPE_OWNER_TYPE.USER
|
||||
) {
|
||||
return MEMORY_SCOPE_BUCKETS.USER_POV;
|
||||
|
||||
Reference in New Issue
Block a user