Files
gemini-cli/packages/core/src/context/graph/fromGraph.ts
T
2026-05-15 05:32:49 +00:00

67 lines
2.0 KiB
TypeScript

/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import type { Content } from '@google/genai';
import type { ConcreteNode } from './types.js';
import { debugLogger } from '../../utils/debugLogger.js';
import type { NodeIdService } from './nodeIdService.js';
import type { HistoryTurn } from '../../core/agentChatHistory.js';
/**
* Reconstructs a list of HistoryTurns from a list of Concrete Nodes.
* This process is "role-alternation-aware" and uses turnId to
* preserve original turn boundaries and IDs.
*/
export function fromGraph(
nodes: readonly ConcreteNode[],
idService?: NodeIdService,
): HistoryTurn[] {
debugLogger.log(
`[fromGraph] Reconstructing history from ${nodes.length} nodes`,
);
const history: HistoryTurn[] = [];
let currentTurn: { id: string; content: Content } | null = null;
for (const node of nodes) {
const turnId = node.turnId || 'orphan'; debugLogger.log("[ID-TRACK] fromGraph converting node:", node.id, "turnId:", turnId);
const durableId = turnId.startsWith('turn_') ? turnId.slice(5) : turnId;
// Register the payload in the identity service to ensure stability
// even if the turn content changes (e.g. after GC backstop).
if (idService) {
idService.set(node.payload, node.id);
}
// We start a new turn if:
// 1. We don't have a current turn.
// 2. The role changes (Standard alternation).
// 3. The turnId changes (Preserving distinct turns of the same role).
if (
!currentTurn ||
currentTurn.content.role !== node.role ||
currentTurn.id !== durableId
) {
currentTurn = {
id: durableId,
content: {
role: node.role,
parts: [node.payload],
},
};
history.push(currentTurn);
} else {
currentTurn.content.parts = [
...(currentTurn.content.parts || []),
node.payload,
];
}
}
debugLogger.log(`[fromGraph] Reconstructed ${history.length} turns`);
return history;
}