diff --git a/packages/cli/src/ui/components/shared/text-buffer.ts b/packages/cli/src/ui/components/shared/text-buffer.ts index 61e6b98e55..05f119f369 100644 --- a/packages/cli/src/ui/components/shared/text-buffer.ts +++ b/packages/cli/src/ui/components/shared/text-buffer.ts @@ -10,6 +10,7 @@ import os from 'node:os'; import pathMod from 'node:path'; import * as path from 'node:path'; import { useState, useCallback, useEffect, useMemo, useReducer } from 'react'; +import { LRUCache } from 'mnemonist'; import { coreEvents, CoreEvent, @@ -18,7 +19,6 @@ import { type EditorType, getEditorCommand, isGuiEditor, - LruCache, } from '@google/gemini-cli-core'; import { toCodePoints, @@ -728,7 +728,7 @@ export function getTransformedImagePath(filePath: string): string { return `[Image ${truncatedBase}${extension}]`; } -const transformationsCache = new LruCache( +const transformationsCache = new LRUCache( LRU_BUFFER_PERF_CACHE_LIMIT, ); @@ -881,7 +881,7 @@ interface LineLayoutResult { visualToTransformedMap: number[]; } -const lineLayoutCache = new LruCache( +const lineLayoutCache = new LRUCache( LRU_BUFFER_PERF_CACHE_LIMIT, ); diff --git a/packages/cli/src/ui/utils/highlight.ts b/packages/cli/src/ui/utils/highlight.ts index 3d182f2451..4d827ee192 100644 --- a/packages/cli/src/ui/utils/highlight.ts +++ b/packages/cli/src/ui/utils/highlight.ts @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { LruCache } from '@google/gemini-cli-core'; +import { LRUCache } from 'mnemonist'; import type { Transformation } from '../components/shared/text-buffer.js'; import { cpLen, cpSlice } from './textUtils.js'; import { LRU_BUFFER_PERF_CACHE_LIMIT } from '../constants.js'; @@ -20,7 +20,7 @@ export type HighlightToken = { // semicolon, common punctuation, and brackets. const HIGHLIGHT_REGEX = /(^\/[a-zA-Z0-9_-]+|@(?:\\ |[^,\s;!?()[\]{}])+)/g; -const highlightCache = new LruCache( +const highlightCache = new LRUCache( LRU_BUFFER_PERF_CACHE_LIMIT, ); diff --git a/packages/cli/src/ui/utils/textUtils.ts b/packages/cli/src/ui/utils/textUtils.ts index 86d345047d..8da60a3821 100644 --- a/packages/cli/src/ui/utils/textUtils.ts +++ b/packages/cli/src/ui/utils/textUtils.ts @@ -8,7 +8,7 @@ import stripAnsi from 'strip-ansi'; import ansiRegex from 'ansi-regex'; import { stripVTControlCharacters } from 'node:util'; import stringWidth from 'string-width'; -import { LruCache } from '@google/gemini-cli-core'; +import { LRUCache } from 'mnemonist'; import { LRU_BUFFER_PERF_CACHE_LIMIT } from '../constants.js'; /** @@ -32,7 +32,7 @@ export const getAsciiArtWidth = (asciiArt: string): number => { // Cache for code points const MAX_STRING_LENGTH_TO_CACHE = 1000; -const codePointsCache = new LruCache( +const codePointsCache = new LRUCache( LRU_BUFFER_PERF_CACHE_LIMIT, ); @@ -123,7 +123,7 @@ export function stripUnsafeCharacters(str: string): string { .join(''); } -const stringWidthCache = new LruCache( +const stringWidthCache = new LRUCache( LRU_BUFFER_PERF_CACHE_LIMIT, ); diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 1a387b5828..506e602ebf 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -92,7 +92,6 @@ export * from './utils/version.js'; export * from './utils/checkpointUtils.js'; export * from './utils/apiConversionUtils.js'; export * from './utils/channel.js'; -export * from './utils/LruCache.js'; // Export services export * from './services/fileDiscoveryService.js'; diff --git a/packages/core/src/utils/LruCache.test.ts b/packages/core/src/utils/LruCache.test.ts deleted file mode 100644 index 10cdbb600c..0000000000 --- a/packages/core/src/utils/LruCache.test.ts +++ /dev/null @@ -1,70 +0,0 @@ -/** - * @license - * Copyright 2025 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import { describe, it, expect } from 'vitest'; -import { LruCache } from './LruCache.js'; - -describe('LruCache', () => { - it('should store and retrieve values', () => { - const cache = new LruCache(10); - cache.set('a', 1); - expect(cache.get('a')).toBe(1); - expect(cache.get('b')).toBeUndefined(); - }); - - it('should evict oldest items when limit is reached', () => { - const cache = new LruCache(2); - cache.set('a', 1); - cache.set('b', 2); - cache.set('c', 3); - - expect(cache.get('a')).toBeUndefined(); - expect(cache.get('b')).toBe(2); - expect(cache.get('c')).toBe(3); - }); - - it('should update LRU order on get', () => { - const cache = new LruCache(2); - cache.set('a', 1); - cache.set('b', 2); - cache.get('a'); // Mark 'a' as recently used - cache.set('c', 3); // Should evict 'b' - - expect(cache.get('b')).toBeUndefined(); - expect(cache.get('a')).toBe(1); - expect(cache.get('c')).toBe(3); - }); - - it('should correctly handle falsy values (truthiness bug fix)', () => { - const cache = new LruCache(2); - cache.set('zero', 0); - cache.set('empty', ''); - - // Both should be in cache - expect(cache.get('zero')).toBe(0); - expect(cache.get('empty')).toBe(''); - - // Access 'zero' to move it to end - cache.get('zero'); - - // Add new item, should evict 'empty' (because 'zero' was just accessed) - cache.set('new', 'item'); - - expect(cache.get('empty')).toBeUndefined(); - expect(cache.get('zero')).toBe(0); - expect(cache.get('new')).toBe('item'); - }); - - it('should correctly handle undefined as a value', () => { - // NOTE: mnemonist LRUMap returns undefined if not found. - // If we store undefined, we can't distinguish between "not found" and "found undefined". - // But we should at least check its behavior. - const cache = new LruCache(10); - cache.set('key', undefined); - expect(cache.has('key')).toBe(true); - expect(cache.get('key')).toBeUndefined(); - }); -}); diff --git a/packages/core/src/utils/LruCache.ts b/packages/core/src/utils/LruCache.ts deleted file mode 100644 index 7c917a8d30..0000000000 --- a/packages/core/src/utils/LruCache.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * @license - * Copyright 2025 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import { LRUMap } from 'mnemonist'; - -export class LruCache { - private cache: LRUMap; - - constructor(maxSize: number) { - this.cache = new LRUMap(maxSize); - } - - get(key: K): V | undefined { - return this.cache.get(key); - } - - has(key: K): boolean { - return this.cache.has(key); - } - - set(key: K, value: V): void { - this.cache.set(key, value); - } - - clear(): void { - this.cache.clear(); - } -} diff --git a/packages/core/src/utils/editCorrector.ts b/packages/core/src/utils/editCorrector.ts index 27e40b8c74..7002114906 100644 --- a/packages/core/src/utils/editCorrector.ts +++ b/packages/core/src/utils/editCorrector.ts @@ -15,7 +15,6 @@ import { READ_MANY_FILES_TOOL_NAME, WRITE_FILE_TOOL_NAME, } from '../tools/tool-names.js'; -import { LruCache } from './LruCache.js'; import { isFunctionResponse, isFunctionCall, @@ -23,6 +22,7 @@ import { import * as fs from 'node:fs'; import { promptIdContext } from './promptIdContext.js'; import { debugLogger } from './debugLogger.js'; +import { LRUCache } from 'mnemonist'; const CODE_CORRECTION_SYSTEM_PROMPT = ` You are an expert code-editing assistant. Your task is to analyze a failed edit attempt and provide a corrected version of the text snippets. @@ -39,12 +39,12 @@ function getPromptId(): string { const MAX_CACHE_SIZE = 50; // Cache for ensureCorrectEdit results -const editCorrectionCache = new LruCache( +const editCorrectionCache = new LRUCache( MAX_CACHE_SIZE, ); // Cache for ensureCorrectFileContent results -const fileContentCorrectionCache = new LruCache(MAX_CACHE_SIZE); +const fileContentCorrectionCache = new LRUCache(MAX_CACHE_SIZE); /** * Defines the structure of the parameters within CorrectedEditResult diff --git a/packages/core/src/utils/llm-edit-fixer.ts b/packages/core/src/utils/llm-edit-fixer.ts index 363c973cd7..591896d715 100644 --- a/packages/core/src/utils/llm-edit-fixer.ts +++ b/packages/core/src/utils/llm-edit-fixer.ts @@ -7,7 +7,7 @@ import { createHash } from 'node:crypto'; import { type Content, Type } from '@google/genai'; import { type BaseLlmClient } from '../core/baseLlmClient.js'; -import { LruCache } from './LruCache.js'; +import { LRUCache } from 'mnemonist'; import { promptIdContext } from './promptIdContext.js'; import { debugLogger } from './debugLogger.js'; @@ -84,7 +84,7 @@ const SearchReplaceEditSchema = { required: ['search', 'replace', 'explanation'], }; -const editCorrectionWithInstructionCache = new LruCache< +const editCorrectionWithInstructionCache = new LRUCache< string, SearchReplaceEdit >(MAX_CACHE_SIZE);