mirror of
https://github.com/Youzini-afk/ST-Bionic-Memory-Ecology.git
synced 2026-06-13 18:31:16 +08:00
fix(recall): drive reroll reuse from host generation context
This commit is contained in:
@@ -99,6 +99,7 @@ function clonePlain(value, fallback = null) {
|
||||
export function createGenerationContextTracker(deps = {}) {
|
||||
let current = null;
|
||||
let pendingSwipe = null;
|
||||
let recentAssistantTailDelete = null;
|
||||
let sequence = 0;
|
||||
|
||||
const now = () =>
|
||||
@@ -123,11 +124,24 @@ export function createGenerationContextTracker(deps = {}) {
|
||||
function begin(type = "normal", params = {}, { dryRun = false, phase = "" } = {}) {
|
||||
if (dryRun) return null;
|
||||
const at = now();
|
||||
const generationType = normalizeGenerationType(type);
|
||||
const kind = classifyGenerationKind(generationType, params);
|
||||
const rawType = normalizeGenerationType(type);
|
||||
const activeChatId = getChatId();
|
||||
const freshInput = Boolean(params?.__stBmeFreshInputHint);
|
||||
const canInferRerollFromDelete = Boolean(
|
||||
rawType === "normal" &&
|
||||
!freshInput &&
|
||||
recentAssistantTailDelete &&
|
||||
recentAssistantTailDelete.chatId === activeChatId &&
|
||||
at - Number(recentAssistantTailDelete.at || 0) <= ttlMs(),
|
||||
);
|
||||
const generationType = canInferRerollFromDelete ? "regenerate" : rawType;
|
||||
const kind = canInferRerollFromDelete
|
||||
? "no-new-user"
|
||||
: classifyGenerationKind(generationType, params);
|
||||
const context = {
|
||||
id: `${at}:${++sequence}`,
|
||||
type: generationType,
|
||||
rawType,
|
||||
kind,
|
||||
chatId: getChatId(),
|
||||
params: clonePlain(params, {}),
|
||||
@@ -142,8 +156,12 @@ export function createGenerationContextTracker(deps = {}) {
|
||||
swipeMeta: generationType === "swipe" ? clonePlain(pendingSwipe?.meta, null) : null,
|
||||
expectedMutation: "",
|
||||
expectedMutationAt: 0,
|
||||
inferredFrom: canInferRerollFromDelete
|
||||
? "assistant-tail-delete-without-fresh-input"
|
||||
: "",
|
||||
};
|
||||
pendingSwipe = null;
|
||||
recentAssistantTailDelete = null;
|
||||
current = context;
|
||||
return { ...context };
|
||||
}
|
||||
@@ -151,7 +169,26 @@ export function createGenerationContextTracker(deps = {}) {
|
||||
function update(type = "normal", params = {}, { dryRun = false, phase = "" } = {}) {
|
||||
if (dryRun) return null;
|
||||
const at = now();
|
||||
const generationType = normalizeGenerationType(type);
|
||||
const rawType = normalizeGenerationType(type);
|
||||
if (
|
||||
current?.inferredFrom &&
|
||||
current.rawType === rawType &&
|
||||
current.chatId === getChatId()
|
||||
) {
|
||||
current = {
|
||||
...current,
|
||||
rawType,
|
||||
params: clonePlain(params, current.params || {}),
|
||||
updatedAt: at,
|
||||
afterCommandsAt:
|
||||
String(phase || "") === "GENERATION_AFTER_COMMANDS"
|
||||
? at
|
||||
: current.afterCommandsAt || 0,
|
||||
phase: String(phase || current.phase || ""),
|
||||
};
|
||||
return { ...current };
|
||||
}
|
||||
const generationType = rawType;
|
||||
const kind = classifyGenerationKind(generationType, params);
|
||||
if (!current || current.type !== generationType || current.chatId !== getChatId()) {
|
||||
return begin(generationType, params, { dryRun, phase });
|
||||
@@ -202,15 +239,26 @@ export function createGenerationContextTracker(deps = {}) {
|
||||
const previous = current;
|
||||
current = null;
|
||||
pendingSwipe = null;
|
||||
recentAssistantTailDelete = null;
|
||||
return previous ? { ...previous, clearReason: String(reason || "") } : null;
|
||||
}
|
||||
|
||||
function noteAssistantTailDelete(payload = {}) {
|
||||
recentAssistantTailDelete = {
|
||||
chatId: getChatId(),
|
||||
at: now(),
|
||||
...clonePlain(payload, {}),
|
||||
};
|
||||
return { ...recentAssistantTailDelete };
|
||||
}
|
||||
|
||||
return {
|
||||
begin,
|
||||
update,
|
||||
get,
|
||||
clear,
|
||||
noteSwipe,
|
||||
noteAssistantTailDelete,
|
||||
markExpectedMutation,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
// ST-BME reroll transaction boundary helpers.
|
||||
//
|
||||
// Pure helpers only. They keep the one-shot reroll recall reuse marker small,
|
||||
// expiring, chat-bound, and tied to an unchanged parent user floor.
|
||||
|
||||
function normalizeText(value = "") {
|
||||
return String(value ?? "").replace(/\r\n/g, "\n").trim();
|
||||
}
|
||||
|
||||
function normalizeChatId(value = "") {
|
||||
return String(value ?? "").trim();
|
||||
}
|
||||
|
||||
function normalizeIndex(value = null) {
|
||||
return Number.isFinite(Number(value)) ? Math.floor(Number(value)) : null;
|
||||
}
|
||||
|
||||
export function createRerollRecallReuseMarker({
|
||||
chatId = "",
|
||||
fromFloor = null,
|
||||
targetUserMessageIndex = null,
|
||||
userText = "",
|
||||
persistedRecord = null,
|
||||
hashRecallInput = null,
|
||||
now = Date.now(),
|
||||
meta = null,
|
||||
} = {}) {
|
||||
const normalizedUserText = normalizeText(userText);
|
||||
if (!normalizedUserText) return { marker: null, reason: "missing-user-text" };
|
||||
|
||||
const persistedInjection = normalizeText(persistedRecord?.injectionText || "");
|
||||
if (!persistedRecord || !persistedInjection) {
|
||||
return { marker: null, reason: "missing-persisted-recall" };
|
||||
}
|
||||
|
||||
const boundText = normalizeText(
|
||||
persistedRecord?.boundUserFloorText || persistedRecord?.recallInput || "",
|
||||
);
|
||||
if (boundText && boundText !== normalizedUserText) {
|
||||
return { marker: null, reason: "bound-user-floor-mismatch" };
|
||||
}
|
||||
|
||||
const hash =
|
||||
typeof hashRecallInput === "function"
|
||||
? hashRecallInput(normalizedUserText)
|
||||
: normalizedUserText;
|
||||
|
||||
return {
|
||||
marker: {
|
||||
chatId: normalizeChatId(chatId),
|
||||
fromFloor: normalizeIndex(fromFloor),
|
||||
targetUserMessageIndex: normalizeIndex(targetUserMessageIndex),
|
||||
userText: normalizedUserText,
|
||||
userHash: String(hash || ""),
|
||||
createdAt: Number(now || 0),
|
||||
meta,
|
||||
},
|
||||
reason: "prepared",
|
||||
};
|
||||
}
|
||||
|
||||
export function consumeRerollRecallReuseMarker({
|
||||
marker = null,
|
||||
activeChatId = "",
|
||||
latestUserMessageIndex = null,
|
||||
currentUserText = "",
|
||||
hashRecallInput = null,
|
||||
now = Date.now(),
|
||||
ttlMs = 0,
|
||||
} = {}) {
|
||||
if (!marker || typeof marker !== "object") {
|
||||
return { consumed: false, marker: null, reason: "missing-marker", override: null };
|
||||
}
|
||||
|
||||
const markerChatId = normalizeChatId(marker.chatId);
|
||||
const normalizedActiveChatId = normalizeChatId(activeChatId);
|
||||
if (markerChatId && normalizedActiveChatId && markerChatId !== normalizedActiveChatId) {
|
||||
return { consumed: false, marker: null, reason: "chat-mismatch", override: null };
|
||||
}
|
||||
|
||||
if (ttlMs > 0 && Number(now || 0) - Number(marker.createdAt || 0) > ttlMs) {
|
||||
return { consumed: false, marker: null, reason: "expired", override: null };
|
||||
}
|
||||
|
||||
const targetUserMessageIndex = normalizeIndex(latestUserMessageIndex);
|
||||
const markerTargetIndex = normalizeIndex(marker.targetUserMessageIndex);
|
||||
if (targetUserMessageIndex !== markerTargetIndex) {
|
||||
return { consumed: false, marker: null, reason: "target-user-floor-changed", override: null };
|
||||
}
|
||||
|
||||
const normalizedUserText = normalizeText(currentUserText);
|
||||
const currentHash =
|
||||
typeof hashRecallInput === "function"
|
||||
? hashRecallInput(normalizedUserText)
|
||||
: normalizedUserText;
|
||||
if (!normalizedUserText || String(currentHash || "") !== String(marker.userHash || "")) {
|
||||
return { consumed: false, marker: null, reason: "user-text-changed", override: null };
|
||||
}
|
||||
|
||||
return {
|
||||
consumed: true,
|
||||
marker: null,
|
||||
reason: "consumed",
|
||||
override: {
|
||||
overrideUserMessage: normalizedUserText,
|
||||
generationType: "normal",
|
||||
targetUserMessageIndex: markerTargetIndex,
|
||||
overrideSource: "chat-last-user",
|
||||
overrideSourceLabel: "历史最后用户楼层",
|
||||
overrideReason: "reroll-user-floor-reuse",
|
||||
sourceCandidates: [
|
||||
{
|
||||
text: normalizedUserText,
|
||||
source: "chat-last-user",
|
||||
sourceLabel: "历史最后用户楼层",
|
||||
reason: "reroll-user-floor-reuse",
|
||||
includeSyntheticUserMessage: false,
|
||||
},
|
||||
],
|
||||
includeSyntheticUserMessage: false,
|
||||
rerollRecallReuse: true,
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user