refac
This commit is contained in:
@@ -225,6 +225,81 @@ export const convertMessagesToHistory = (messages) => {
|
||||
return history;
|
||||
};
|
||||
|
||||
// Repair structurally-malformed history nodes from failed regenerations.
|
||||
// A lost assistant placeholder may exist under its map key with only
|
||||
// completion fields (content/done/error), missing id/role/parentId.
|
||||
// Reconstruct graph fields so already-corrupted chats recover on open.
|
||||
export const sanitizeHistory = (history) => {
|
||||
if (!history?.messages || typeof history.messages !== 'object') return;
|
||||
|
||||
// Purge entries that aren't usable objects
|
||||
for (const [id, message] of Object.entries(history.messages)) {
|
||||
if (!message || typeof message !== 'object') {
|
||||
delete history.messages[id];
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure every surviving node has its canonical id and a childrenIds array
|
||||
for (const [id, message] of Object.entries(history.messages)) {
|
||||
if (message.id !== id) message.id = id;
|
||||
if (!Array.isArray(message.childrenIds)) message.childrenIds = [];
|
||||
}
|
||||
|
||||
// Build reverse lookup: parent, indexed by child id
|
||||
const parentByChildId = {};
|
||||
for (const [id, message] of Object.entries(history.messages)) {
|
||||
for (const childId of message.childrenIds) {
|
||||
parentByChildId[childId] = id;
|
||||
}
|
||||
}
|
||||
|
||||
// Reconstruct missing parentId and role
|
||||
for (const [id, message] of Object.entries(history.messages)) {
|
||||
// Well-formed: has role and explicit parentId (null is valid for root)
|
||||
if (message.role && message.parentId !== undefined) continue;
|
||||
|
||||
if (message.parentId === undefined) {
|
||||
message.parentId = parentByChildId[id] ?? null;
|
||||
}
|
||||
|
||||
if (!message.role) {
|
||||
const parent = message.parentId ? history.messages[message.parentId] : null;
|
||||
message.role =
|
||||
parent?.role === 'user'
|
||||
? 'assistant'
|
||||
: parent?.role === 'assistant'
|
||||
? 'user'
|
||||
: message.model || message.usage || message.done !== undefined
|
||||
? 'assistant'
|
||||
: 'user';
|
||||
}
|
||||
}
|
||||
|
||||
// Prune childrenIds referencing deleted/missing nodes
|
||||
for (const message of Object.values(history.messages)) {
|
||||
message.childrenIds = message.childrenIds.filter(
|
||||
(childId) => history.messages[childId]
|
||||
);
|
||||
}
|
||||
|
||||
// Recover currentId if it points to a missing or incomplete node
|
||||
const currentMessage = history.messages?.[history.currentId];
|
||||
if (!currentMessage?.id || !currentMessage?.role) {
|
||||
let latestLeafId = null;
|
||||
let latestTimestamp = -1;
|
||||
for (const [id, message] of Object.entries(history.messages)) {
|
||||
if (
|
||||
message.childrenIds.length === 0 &&
|
||||
(message.timestamp ?? 0) > latestTimestamp
|
||||
) {
|
||||
latestLeafId = id;
|
||||
latestTimestamp = message.timestamp ?? 0;
|
||||
}
|
||||
}
|
||||
history.currentId = latestLeafId ?? Object.keys(history.messages)[0] ?? null;
|
||||
}
|
||||
};
|
||||
|
||||
export const getGravatarURL = (email) => {
|
||||
// Trim leading and trailing whitespace from
|
||||
// an email address and force all characters
|
||||
|
||||
Reference in New Issue
Block a user