Roll back aggressive chat reuse paths that froze message parts, tool state, and completed assistant bodies when sessions were inactive or backgrounded.
79 lines
2.3 KiB
TypeScript
79 lines
2.3 KiB
TypeScript
import { projectTurnIndexes } from './projectTurnIndexes';
|
|
import type { TurnProjectionResult, TurnRecord } from './types';
|
|
|
|
const areTurnMessagesReferenceStable = (previousTurn: TurnRecord, nextTurn: TurnRecord): boolean => {
|
|
if (previousTurn.userMessage !== nextTurn.userMessage) {
|
|
return false;
|
|
}
|
|
|
|
if (previousTurn.assistantMessages.length !== nextTurn.assistantMessages.length) {
|
|
return false;
|
|
}
|
|
|
|
for (let index = 0; index < previousTurn.assistantMessages.length; index += 1) {
|
|
if (previousTurn.assistantMessages[index] !== nextTurn.assistantMessages[index]) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
const buildTurnSignature = (turn: TurnRecord): string => {
|
|
const assistantIds = turn.assistantMessageIds.join(',');
|
|
return [
|
|
turn.turnId,
|
|
turn.headerMessageId ?? '',
|
|
assistantIds,
|
|
turn.summaryText ?? '',
|
|
turn.stream.isStreaming ? '1' : '0',
|
|
turn.stream.isRetrying ? '1' : '0',
|
|
turn.completedAt ?? '',
|
|
].join('|');
|
|
};
|
|
|
|
export const stabilizeTurnProjection = (
|
|
nextProjection: TurnProjectionResult,
|
|
previousProjection: TurnProjectionResult | null,
|
|
): TurnProjectionResult => {
|
|
if (!previousProjection || previousProjection.turns.length === 0 || nextProjection.turns.length === 0) {
|
|
return nextProjection;
|
|
}
|
|
|
|
const previousById = new Map(previousProjection.turns.map((turn) => [turn.turnId, turn]));
|
|
let reused = false;
|
|
|
|
const stabilizedTurns = nextProjection.turns.map((turn, index) => {
|
|
const isLastTurn = index === nextProjection.turns.length - 1;
|
|
if (isLastTurn) {
|
|
return turn;
|
|
}
|
|
|
|
const previousTurn = previousById.get(turn.turnId);
|
|
if (!previousTurn) {
|
|
return turn;
|
|
}
|
|
|
|
if (buildTurnSignature(previousTurn) !== buildTurnSignature(turn)) {
|
|
return turn;
|
|
}
|
|
|
|
if (!areTurnMessagesReferenceStable(previousTurn, turn)) {
|
|
return turn;
|
|
}
|
|
|
|
reused = true;
|
|
return previousTurn;
|
|
});
|
|
|
|
if (!reused) {
|
|
return nextProjection;
|
|
}
|
|
|
|
const projection = projectTurnIndexes(stabilizedTurns);
|
|
return {
|
|
...projection,
|
|
ungroupedMessageIds: nextProjection.ungroupedMessageIds,
|
|
};
|
|
};
|