UNPKG

autosnippet

Version:

Extract code patterns into a knowledge base for AI coding assistants

162 lines (161 loc) 6.98 kB
/** * IncrementalBootstrap — 增量冷启动控制器 * * 基于 BootstrapSnapshot 存储的文件指纹,检测项目变更范围, * 推断受影响维度,并控制 fillDimensionsV3 仅执行受影响维度。 * * 流程: * 1. 加载上次成功快照 * 2. 扫描当前文件 → 计算 diff (added/modified/deleted) * 3. 推断受影响维度 → { mode, dimensions, skippedDimensions } * 4. 从快照恢复未变更维度的 EpisodicMemory * 5. 只对受影响维度执行 Analyst → Producer * 6. 完成后保存新快照 * * @module pipeline/IncrementalBootstrap */ import { SessionStore } from '#agent/memory/SessionStore.js'; import { BootstrapSnapshot } from './BootstrapSnapshot.js'; // ────────────────────────────────────────────────────────────── // IncrementalBootstrap 类 // ────────────────────────────────────────────────────────────── export class IncrementalBootstrap { #snapshot; #logger; #projectRoot; constructor(db, projectRoot, { logger } = {}) { this.#snapshot = new BootstrapSnapshot(db, { logger }); this.#logger = logger || null; this.#projectRoot = projectRoot; } /** * 评估增量可行性 — 在 bootstrap 流程最开始调用 * * @param currentFiles 当前扫描到的文件 * @param allDimIds 所有可用维度 ID */ evaluate(currentFiles, allDimIds) { try { // 1. 加载上次快照 const previousSnapshot = this.#snapshot.getLatest(this.#projectRoot); if (!previousSnapshot) { this.#log('No previous snapshot found — full bootstrap required'); return { canIncremental: false, mode: 'full', affectedDimensions: allDimIds, skippedDimensions: [], previousSnapshot: null, diff: null, reason: '无历史快照,需要全量冷启动', restoredEpisodic: null, }; } // 2. 计算 diff const diff = this.#snapshot.computeDiff(previousSnapshot, currentFiles, this.#projectRoot); this.#log(`Diff: +${diff.added.length} added, ~${diff.modified.length} modified, ` + `-${diff.deleted.length} deleted, =${diff.unchanged.length} unchanged ` + `(ratio: ${(diff.changeRatio * 100).toFixed(1)}%)`); // 3. 推断受影响维度 const inference = this.#snapshot.inferAffectedDimensions(previousSnapshot, diff, allDimIds); if (inference.mode === 'full') { this.#log(`Full rebuild recommended: ${inference.reason}`); return { canIncremental: false, mode: 'full', affectedDimensions: allDimIds, skippedDimensions: [], previousSnapshot, diff, reason: inference.reason, restoredEpisodic: null, }; } // 4. 增量可行 → 尝试恢复 SessionStore let restoredEpisodic = null; if (previousSnapshot.episodicData) { try { restoredEpisodic = SessionStore.fromJSON(previousSnapshot.episodicData); this.#log(`Restored SessionStore: ${restoredEpisodic.getCompletedDimensions().length} dimensions`); } catch (err) { this.#log(`Failed to restore SessionStore: ${err instanceof Error ? err.message : String(err)}`, 'warn'); } } this.#log(`Incremental plan: ${inference.dimensions.length} affected, ` + `${inference.skippedDimensions.length} skipped — ${inference.reason}`); return { canIncremental: true, mode: 'incremental', affectedDimensions: inference.dimensions, skippedDimensions: inference.skippedDimensions, previousSnapshot, diff, reason: inference.reason, restoredEpisodic, }; } catch (err) { const errMsg = err instanceof Error ? err.message : String(err); this.#log(`Incremental evaluation failed: ${errMsg} — fallback to full`, 'warn'); return { canIncremental: false, mode: 'full', affectedDimensions: allDimIds, skippedDimensions: [], previousSnapshot: null, diff: null, reason: `增量评估失败 (${errMsg}),回退全量`, restoredEpisodic: null, }; } } /** * 保存快照 — 在 bootstrap 完成后调用 * * @param [params.meta] { durationMs, candidateCount, primaryLang } * @param [params.plan] evaluate() 返回的计划 (增量时) * @returns 快照 ID */ saveSnapshot(params) { const { sessionId, allFiles, dimensionStats, episodicMemory, meta = {}, plan = null } = params; // 构建带 referencedFilesList 的 dimensionStats const enrichedStats = { ...dimensionStats }; if (episodicMemory) { for (const dimId of episodicMemory.getCompletedDimensions()) { const report = episodicMemory.getDimensionReport?.(dimId); if (report && enrichedStats[dimId]) { enrichedStats[dimId] = { ...enrichedStats[dimId], referencedFilesList: report.referencedFiles || [], }; } } } return this.#snapshot.save({ sessionId, projectRoot: this.#projectRoot, allFiles, dimensionStats: enrichedStats, episodicData: episodicMemory?.toJSON() || null, meta, isIncremental: plan?.mode === 'incremental', parentId: plan?.previousSnapshot?.id || null, changedFiles: plan?.diff ? [...(plan.diff.added || []), ...(plan.diff.modified || []), ...(plan.diff.deleted || [])] : [], affectedDims: plan?.affectedDimensions || [], }); } /** 获取快照管理器 (用于直接查询) */ getSnapshotManager() { return this.#snapshot; } #log(msg, level = 'info') { if (this.#logger) { const fn = this.#logger[level]; fn?.(`[IncrementalBootstrap] ${msg}`); } } } export default IncrementalBootstrap;