feat: move LLM cache to project-local directory
- Cache now lives in <project>/.pi-project-map/cache/llm-cache.json - Removed global ~/.cache/pi-project-map/ usage - Cache travels with the project, no cross-project collisions - Easy to invalidate: rm -rf .pi-project-map/cache/ Files changed: - llm-cache.ts: accept cacheDir parameter, default to project-local - llm-extract.ts: pass cacheDir through to cache functions - init.ts: pass rootPath as cacheDir - patch.ts: accept and pass cacheDir - cli.ts: pass targetPath as cacheDir - pi-extension.ts: pass ctx.cwd as cacheDir - Tests updated to use temp dirs for cache isolation
This commit is contained in:
+9
-4
@@ -2,7 +2,12 @@ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|||||||
import { Type } from "typebox";
|
import { Type } from "typebox";
|
||||||
import { readFileSync, readdirSync, statSync } from "fs";
|
import { readFileSync, readdirSync, statSync } from "fs";
|
||||||
import { join, relative } from "path";
|
import { join, relative } from "path";
|
||||||
import { initProject, patchFile, validateMaps, reinitPath } from "./src/index.js";
|
import {
|
||||||
|
initProject,
|
||||||
|
patchFile,
|
||||||
|
validateMaps,
|
||||||
|
reinitPath,
|
||||||
|
} from "./src/index.js";
|
||||||
import { createLLMClient } from "./src/llm-client.js";
|
import { createLLMClient } from "./src/llm-client.js";
|
||||||
import { LLMError } from "./src/llm-error.js";
|
import { LLMError } from "./src/llm-error.js";
|
||||||
|
|
||||||
@@ -73,7 +78,7 @@ export default function (pi: ExtensionAPI) {
|
|||||||
try {
|
try {
|
||||||
const targetPath = params.path || ctx.cwd;
|
const targetPath = params.path || ctx.cwd;
|
||||||
const client = getPiLLMClient(ctx);
|
const client = getPiLLMClient(ctx);
|
||||||
await initProject(targetPath, { verbose: false, llmClient: client });
|
await initProject(targetPath, { verbose: false, llmClient: client, cacheDir: ctx.cwd });
|
||||||
return {
|
return {
|
||||||
content: [
|
content: [
|
||||||
{
|
{
|
||||||
@@ -111,7 +116,7 @@ export default function (pi: ExtensionAPI) {
|
|||||||
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
||||||
try {
|
try {
|
||||||
const client = getPiLLMClient(ctx);
|
const client = getPiLLMClient(ctx);
|
||||||
await patchFile(params.file_path, client);
|
await patchFile(params.file_path, client, ctx.cwd);
|
||||||
return {
|
return {
|
||||||
content: [
|
content: [
|
||||||
{
|
{
|
||||||
@@ -194,7 +199,7 @@ export default function (pi: ExtensionAPI) {
|
|||||||
try {
|
try {
|
||||||
const targetPath = params.path || ctx.cwd;
|
const targetPath = params.path || ctx.cwd;
|
||||||
const client = getPiLLMClient(ctx);
|
const client = getPiLLMClient(ctx);
|
||||||
await reinitPath(targetPath, { verbose: false, llmClient: client });
|
await reinitPath(targetPath, { verbose: false, llmClient: client, cacheDir: ctx.cwd });
|
||||||
return {
|
return {
|
||||||
content: [
|
content: [
|
||||||
{
|
{
|
||||||
|
|||||||
+3
-3
@@ -128,7 +128,7 @@ async function main() {
|
|||||||
const entries = discoverProject(targetPath);
|
const entries = discoverProject(targetPath);
|
||||||
console.log(`Scanning ${formatCount(entries.length, "directory")}...`);
|
console.log(`Scanning ${formatCount(entries.length, "directory")}...`);
|
||||||
const client = createClientFromArgs(parsed);
|
const client = createClientFromArgs(parsed);
|
||||||
await initProject(targetPath, { verbose: false, llmClient: client });
|
await initProject(targetPath, { verbose: false, llmClient: client, cacheDir: targetPath });
|
||||||
const elapsed = ((Date.now() - start) / 1000).toFixed(1);
|
const elapsed = ((Date.now() - start) / 1000).toFixed(1);
|
||||||
console.log(
|
console.log(
|
||||||
`${pc.green("✓")} Generated ${formatCount(entries.length, ".pi-map.md file")} in ${elapsed}s`,
|
`${pc.green("✓")} Generated ${formatCount(entries.length, ".pi-map.md file")} in ${elapsed}s`,
|
||||||
@@ -143,7 +143,7 @@ async function main() {
|
|||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
const client = createClientFromArgs(parsed);
|
const client = createClientFromArgs(parsed);
|
||||||
await patchFile(parsed.positional[0], client);
|
await patchFile(parsed.positional[0], client, process.cwd());
|
||||||
console.log(`${pc.green("✓")} Patched`);
|
console.log(`${pc.green("✓")} Patched`);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -179,7 +179,7 @@ async function main() {
|
|||||||
`Regenerating ${formatCount(entries.length, ".pi-map.md file")}...`,
|
`Regenerating ${formatCount(entries.length, ".pi-map.md file")}...`,
|
||||||
);
|
);
|
||||||
const client = createClientFromArgs(parsed);
|
const client = createClientFromArgs(parsed);
|
||||||
await reinitPath(targetPath, { verbose: false, llmClient: client });
|
await reinitPath(targetPath, { verbose: false, llmClient: client, cacheDir: targetPath });
|
||||||
const elapsed = ((Date.now() - start) / 1000).toFixed(1);
|
const elapsed = ((Date.now() - start) / 1000).toFixed(1);
|
||||||
console.log(`${pc.green("✓")} Regenerated in ${elapsed}s`);
|
console.log(`${pc.green("✓")} Regenerated in ${elapsed}s`);
|
||||||
break;
|
break;
|
||||||
|
|||||||
+5
-2
@@ -14,6 +14,7 @@ import type { LLMClient } from "./llm-client.js";
|
|||||||
export interface InitOptions {
|
export interface InitOptions {
|
||||||
verbose?: boolean;
|
verbose?: boolean;
|
||||||
llmClient?: LLMClient;
|
llmClient?: LLMClient;
|
||||||
|
cacheDir?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function initProject(
|
export async function initProject(
|
||||||
@@ -23,7 +24,7 @@ export async function initProject(
|
|||||||
const entries = discoverProject(rootPath);
|
const entries = discoverProject(rootPath);
|
||||||
|
|
||||||
for (const entry of entries) {
|
for (const entry of entries) {
|
||||||
await generateDirectoryMap(entry, options.llmClient);
|
await generateDirectoryMap(entry, options.llmClient, options.cacheDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.verbose !== false) {
|
if (options.verbose !== false) {
|
||||||
@@ -34,11 +35,12 @@ export async function initProject(
|
|||||||
export async function generateDirectoryMap(
|
export async function generateDirectoryMap(
|
||||||
entry: DirectoryEntry,
|
entry: DirectoryEntry,
|
||||||
llmClient?: LLMClient,
|
llmClient?: LLMClient,
|
||||||
|
cacheDir?: string,
|
||||||
): Promise<FileEntry[]> {
|
): Promise<FileEntry[]> {
|
||||||
const fileData: FileEntry[] = [];
|
const fileData: FileEntry[] = [];
|
||||||
for (const file of entry.files) {
|
for (const file of entry.files) {
|
||||||
const filePath = join(entry.dirPath, file);
|
const filePath = join(entry.dirPath, file);
|
||||||
const llmData = await extractFileLLM(filePath, llmClient);
|
const llmData = await extractFileLLM(filePath, llmClient, cacheDir);
|
||||||
const astData = await extractFileAST(filePath);
|
const astData = await extractFileAST(filePath);
|
||||||
fileData.push(mergeFileData(file, llmData, astData));
|
fileData.push(mergeFileData(file, llmData, astData));
|
||||||
}
|
}
|
||||||
@@ -47,6 +49,7 @@ export async function generateDirectoryMap(
|
|||||||
entry.relativePath,
|
entry.relativePath,
|
||||||
fileData,
|
fileData,
|
||||||
llmClient,
|
llmClient,
|
||||||
|
cacheDir,
|
||||||
);
|
);
|
||||||
|
|
||||||
const mapData: PackageMapData = {
|
const mapData: PackageMapData = {
|
||||||
|
|||||||
+28
-18
@@ -1,25 +1,33 @@
|
|||||||
import { readFileSync, writeFileSync, existsSync, mkdirSync, renameSync } from "fs";
|
import { readFileSync, writeFileSync, existsSync, mkdirSync, renameSync } from "fs";
|
||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
import { homedir } from "os";
|
|
||||||
|
|
||||||
const CACHE_DIR = join(homedir(), ".cache", "pi-project-map");
|
const DEFAULT_CACHE_SUBDIR = ".pi-project-map";
|
||||||
const CACHE_FILE = join(CACHE_DIR, "llm-cache.json");
|
const DEFAULT_CACHE_FILE = "cache/llm-cache.json";
|
||||||
|
|
||||||
interface CacheEntry {
|
interface CacheEntry {
|
||||||
result: string;
|
result: string;
|
||||||
ts: number;
|
ts: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ensureCacheDir(): void {
|
function getCachePath(cacheDir?: string): string {
|
||||||
if (!existsSync(CACHE_DIR)) {
|
if (cacheDir) {
|
||||||
mkdirSync(CACHE_DIR, { recursive: true });
|
return join(cacheDir, DEFAULT_CACHE_FILE);
|
||||||
|
}
|
||||||
|
// Fallback to cwd when no project root provided
|
||||||
|
return join(process.cwd(), DEFAULT_CACHE_SUBDIR, DEFAULT_CACHE_FILE);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureCacheDir(cachePath: string): void {
|
||||||
|
const dir = cachePath.substring(0, cachePath.lastIndexOf("/"));
|
||||||
|
if (!existsSync(dir)) {
|
||||||
|
mkdirSync(dir, { recursive: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadCache(): Record<string, CacheEntry> {
|
function loadCache(cachePath: string): Record<string, CacheEntry> {
|
||||||
if (!existsSync(CACHE_FILE)) return {};
|
if (!existsSync(cachePath)) return {};
|
||||||
try {
|
try {
|
||||||
const raw = readFileSync(CACHE_FILE, "utf8");
|
const raw = readFileSync(cachePath, "utf8");
|
||||||
return JSON.parse(raw) as Record<string, CacheEntry>;
|
return JSON.parse(raw) as Record<string, CacheEntry>;
|
||||||
} catch {
|
} catch {
|
||||||
// Corrupted cache — start fresh
|
// Corrupted cache — start fresh
|
||||||
@@ -27,20 +35,22 @@ function loadCache(): Record<string, CacheEntry> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveCache(cache: Record<string, CacheEntry>): void {
|
function saveCache(cachePath: string, cache: Record<string, CacheEntry>): void {
|
||||||
ensureCacheDir();
|
ensureCacheDir(cachePath);
|
||||||
const tmp = CACHE_FILE + ".tmp";
|
const tmp = `${cachePath}.tmp`;
|
||||||
writeFileSync(tmp, JSON.stringify(cache, null, 2));
|
writeFileSync(tmp, JSON.stringify(cache, null, 2));
|
||||||
renameSync(tmp, CACHE_FILE);
|
renameSync(tmp, cachePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getCached(hash: string): string | undefined {
|
export function getCached(hash: string, cacheDir?: string): string | undefined {
|
||||||
const cache = loadCache();
|
const cachePath = getCachePath(cacheDir);
|
||||||
|
const cache = loadCache(cachePath);
|
||||||
return cache[hash]?.result;
|
return cache[hash]?.result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setCached(hash: string, result: string): void {
|
export function setCached(hash: string, result: string, cacheDir?: string): void {
|
||||||
const cache = loadCache();
|
const cachePath = getCachePath(cacheDir);
|
||||||
|
const cache = loadCache(cachePath);
|
||||||
cache[hash] = { result, ts: Date.now() };
|
cache[hash] = { result, ts: Date.now() };
|
||||||
saveCache(cache);
|
saveCache(cachePath, cache);
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-2
@@ -138,12 +138,13 @@ function parsePackageResponse(response: string): { role: string; arch: string }
|
|||||||
export async function extractFileLLM(
|
export async function extractFileLLM(
|
||||||
filePath: string,
|
filePath: string,
|
||||||
client?: LLMClient,
|
client?: LLMClient,
|
||||||
|
cacheDir?: string,
|
||||||
): Promise<LLMFileData> {
|
): Promise<LLMFileData> {
|
||||||
const content = readFileSync(filePath, "utf8");
|
const content = readFileSync(filePath, "utf8");
|
||||||
const hash = createHash("sha256").update(content).digest("hex");
|
const hash = createHash("sha256").update(content).digest("hex");
|
||||||
|
|
||||||
// Check disk cache
|
// Check disk cache
|
||||||
const cached = getCached(hash);
|
const cached = getCached(hash, cacheDir);
|
||||||
if (cached) {
|
if (cached) {
|
||||||
const parsed = parseFileResponse(cached);
|
const parsed = parseFileResponse(cached);
|
||||||
return {
|
return {
|
||||||
@@ -172,7 +173,7 @@ export async function extractFileLLM(
|
|||||||
|
|
||||||
const prompt = buildFilePrompt(filePath, truncateForContext(content, 200));
|
const prompt = buildFilePrompt(filePath, truncateForContext(content, 200));
|
||||||
const response = await client.complete(prompt);
|
const response = await client.complete(prompt);
|
||||||
setCached(hash, response);
|
setCached(hash, response, cacheDir);
|
||||||
|
|
||||||
const parsed = parseFileResponse(response);
|
const parsed = parseFileResponse(response);
|
||||||
return {
|
return {
|
||||||
@@ -187,6 +188,7 @@ export async function extractPackageLLM(
|
|||||||
relativePath: string,
|
relativePath: string,
|
||||||
fileData: { name: string; purpose: string }[],
|
fileData: { name: string; purpose: string }[],
|
||||||
client?: LLMClient,
|
client?: LLMClient,
|
||||||
|
_cacheDir?: string,
|
||||||
): Promise<LLMPackageData> {
|
): Promise<LLMPackageData> {
|
||||||
if (!client) {
|
if (!client) {
|
||||||
return extractPackageHeuristic(relativePath, fileData);
|
return extractPackageHeuristic(relativePath, fileData);
|
||||||
|
|||||||
+3
-1
@@ -13,6 +13,7 @@ const SMALL_PACKAGE_THRESHOLD = 10;
|
|||||||
export async function patchFile(
|
export async function patchFile(
|
||||||
filePath: string,
|
filePath: string,
|
||||||
llmClient?: LLMClient,
|
llmClient?: LLMClient,
|
||||||
|
cacheDir?: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const dirPath = dirname(filePath);
|
const dirPath = dirname(filePath);
|
||||||
const mapPath = join(dirPath, ".pi-map.md");
|
const mapPath = join(dirPath, ".pi-map.md");
|
||||||
@@ -42,6 +43,7 @@ export async function patchFile(
|
|||||||
files,
|
files,
|
||||||
},
|
},
|
||||||
llmClient,
|
llmClient,
|
||||||
|
cacheDir,
|
||||||
);
|
);
|
||||||
console.log(
|
console.log(
|
||||||
`Full rewrite of ${mapPath} (small package: ${allFiles.length} files)`,
|
`Full rewrite of ${mapPath} (small package: ${allFiles.length} files)`,
|
||||||
@@ -49,7 +51,7 @@ export async function patchFile(
|
|||||||
} else {
|
} else {
|
||||||
// Section-level patch
|
// Section-level patch
|
||||||
const existing = parsePackageMap(readFileSync(mapPath, "utf8"));
|
const existing = parsePackageMap(readFileSync(mapPath, "utf8"));
|
||||||
const llmData = await extractFileLLM(filePath, llmClient);
|
const llmData = await extractFileLLM(filePath, llmClient, cacheDir);
|
||||||
const astData = await extractFileAST(filePath);
|
const astData = await extractFileAST(filePath);
|
||||||
const fileName = basename(filePath);
|
const fileName = basename(filePath);
|
||||||
const updatedFile = mergeFileData(fileName, llmData, astData);
|
const updatedFile = mergeFileData(fileName, llmData, astData);
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
||||||
import { getCached, setCached } from "../src/llm-cache.js";
|
import { getCached, setCached } from "../src/llm-cache.js";
|
||||||
import { existsSync, unlinkSync, rmdirSync } from "fs";
|
import { existsSync, unlinkSync } from "fs";
|
||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
import { homedir } from "os";
|
import { tmpdir } from "os";
|
||||||
|
|
||||||
const TEST_CACHE_DIR = join(homedir(), ".cache", "pi-project-map");
|
const TEST_CACHE_DIR = join(tmpdir(), "pi-project-map-test-cache");
|
||||||
const TEST_CACHE_FILE = join(TEST_CACHE_DIR, "llm-cache.json");
|
const TEST_CACHE_FILE = join(TEST_CACHE_DIR, "llm-cache.json");
|
||||||
|
|
||||||
describe("llm-cache", () => {
|
describe("llm-cache", () => {
|
||||||
@@ -21,20 +21,20 @@ describe("llm-cache", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("returns undefined for missing entries", () => {
|
it("returns undefined for missing entries", () => {
|
||||||
const result = getCached("nonexistent-hash");
|
const result = getCached("nonexistent-hash", TEST_CACHE_DIR);
|
||||||
expect(result).toBeUndefined();
|
expect(result).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("stores and retrieves cached results", () => {
|
it("stores and retrieves cached results", () => {
|
||||||
setCached("abc123", "PURPOSE: test\nDEPS: none\nCONCEPTS: none");
|
setCached("abc123", "PURPOSE: test\nDEPS: none\nCONCEPTS: none", TEST_CACHE_DIR);
|
||||||
const result = getCached("abc123");
|
const result = getCached("abc123", TEST_CACHE_DIR);
|
||||||
expect(result).toBe("PURPOSE: test\nDEPS: none\nCONCEPTS: none");
|
expect(result).toBe("PURPOSE: test\nDEPS: none\nCONCEPTS: none");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("overwrites existing entries", () => {
|
it("overwrites existing entries", () => {
|
||||||
setCached("abc123", "old");
|
setCached("abc123", "old", TEST_CACHE_DIR);
|
||||||
setCached("abc123", "new");
|
setCached("abc123", "new", TEST_CACHE_DIR);
|
||||||
const result = getCached("abc123");
|
const result = getCached("abc123", TEST_CACHE_DIR);
|
||||||
expect(result).toBe("new");
|
expect(result).toBe("new");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ describe("llm-extract with mock client", () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = await extractFileLLM(file, mockClient);
|
const result = await extractFileLLM(file, mockClient, tmpdir());
|
||||||
expect(result.purpose).toBe("Test file");
|
expect(result.purpose).toBe("Test file");
|
||||||
expect(result.deps).toEqual([]);
|
expect(result.deps).toEqual([]);
|
||||||
expect(result.concepts).toContain("testing");
|
expect(result.concepts).toContain("testing");
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ describe.skipIf(!hasKimiKey)("LLM integration with Kimi", () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const client = createLLMClient("kimi", { model: kimiModel });
|
const client = createLLMClient("kimi", { model: kimiModel });
|
||||||
const result = await extractFileLLM(file, client);
|
const result = await extractFileLLM(file, client, dir);
|
||||||
|
|
||||||
expect(result.purpose).toBeTruthy();
|
expect(result.purpose).toBeTruthy();
|
||||||
expect(result.purpose.length).toBeGreaterThan(5);
|
expect(result.purpose.length).toBeGreaterThan(5);
|
||||||
@@ -90,12 +90,12 @@ describe.skipIf(!hasKimiKey)("LLM integration with Kimi", () => {
|
|||||||
writeFileSync(file, `export const version = "1.0.0";`);
|
writeFileSync(file, `export const version = "1.0.0";`);
|
||||||
|
|
||||||
const client = createLLMClient("kimi", { model: kimiModel });
|
const client = createLLMClient("kimi", { model: kimiModel });
|
||||||
const result1 = await extractFileLLM(file, client);
|
const result1 = await extractFileLLM(file, client, dir);
|
||||||
expect(result1.purpose).toBeTruthy();
|
expect(result1.purpose).toBeTruthy();
|
||||||
|
|
||||||
// Second call should hit cache — much faster
|
// Second call should hit cache — much faster
|
||||||
const start = Date.now();
|
const start = Date.now();
|
||||||
const result2 = await extractFileLLM(file, client);
|
const result2 = await extractFileLLM(file, client, dir);
|
||||||
const elapsed = Date.now() - start;
|
const elapsed = Date.now() - start;
|
||||||
|
|
||||||
expect(result2.purpose).toBe(result1.purpose);
|
expect(result2.purpose).toBe(result1.purpose);
|
||||||
@@ -119,7 +119,7 @@ describe.skipIf(!hasKimiKey)("LLM integration with Kimi", () => {
|
|||||||
const start = Date.now();
|
const start = Date.now();
|
||||||
const results = await processFiles(
|
const results = await processFiles(
|
||||||
files,
|
files,
|
||||||
async (f) => extractFileLLM(f, client),
|
async (f) => extractFileLLM(f, client, dir),
|
||||||
{ concurrency: 3, maxRetries: 1, retryDelaysMs: [2000] },
|
{ concurrency: 3, maxRetries: 1, retryDelaysMs: [2000] },
|
||||||
);
|
);
|
||||||
const elapsed = Date.now() - start;
|
const elapsed = Date.now() - start;
|
||||||
@@ -145,7 +145,7 @@ describe.skipIf(!hasKimiKey)("LLM integration with Kimi", () => {
|
|||||||
return originalComplete(...args);
|
return originalComplete(...args);
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = await extractFileLLM(file, trackingClient);
|
const result = await extractFileLLM(file, trackingClient, dir);
|
||||||
expect(result.purpose).toBe("Large/generated file");
|
expect(result.purpose).toBe("Large/generated file");
|
||||||
expect(calls).toBe(0); // Should never call LLM for large files
|
expect(calls).toBe(0); // Should never call LLM for large files
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -19,13 +19,23 @@ vi.mock("typebox", () => ({
|
|||||||
vi.mock("@mariozechner/pi-ai", () => ({
|
vi.mock("@mariozechner/pi-ai", () => ({
|
||||||
complete: vi.fn(async (_model: any, _context: any) => ({
|
complete: vi.fn(async (_model: any, _context: any) => ({
|
||||||
role: "assistant" as const,
|
role: "assistant" as const,
|
||||||
content: [{ type: "text", text: "PURPOSE: Test file\nDEPS: none\nCONCEPTS: testing" }],
|
content: [
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
text: "PURPOSE: Test file\nDEPS: none\nCONCEPTS: testing",
|
||||||
|
},
|
||||||
|
],
|
||||||
})),
|
})),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
import extension from "../pi-extension.js";
|
import extension from "../pi-extension.js";
|
||||||
|
|
||||||
const CACHE_FILE = join(homedir(), ".cache", "pi-project-map", "llm-cache.json");
|
const CACHE_FILE = join(
|
||||||
|
homedir(),
|
||||||
|
".cache",
|
||||||
|
"pi-project-map",
|
||||||
|
"llm-cache.json",
|
||||||
|
);
|
||||||
function clearCache() {
|
function clearCache() {
|
||||||
if (existsSync(CACHE_FILE)) unlinkSync(CACHE_FILE);
|
if (existsSync(CACHE_FILE)) unlinkSync(CACHE_FILE);
|
||||||
}
|
}
|
||||||
@@ -43,9 +53,16 @@ describe("pi-extension", () => {
|
|||||||
mockNotify = vi.fn();
|
mockNotify = vi.fn();
|
||||||
mockCtx = {
|
mockCtx = {
|
||||||
cwd: "/home/project",
|
cwd: "/home/project",
|
||||||
model: { provider: "openai", id: "gpt-4o-mini", api: "openai-completions" },
|
model: {
|
||||||
|
provider: "openai",
|
||||||
|
id: "gpt-4o-mini",
|
||||||
|
api: "openai-completions",
|
||||||
|
},
|
||||||
modelRegistry: {
|
modelRegistry: {
|
||||||
getApiKeyAndHeaders: vi.fn(async () => ({ apiKey: "test-key", headers: {} })),
|
getApiKeyAndHeaders: vi.fn(async () => ({
|
||||||
|
apiKey: "test-key",
|
||||||
|
headers: {},
|
||||||
|
})),
|
||||||
},
|
},
|
||||||
ui: { notify: mockNotify },
|
ui: { notify: mockNotify },
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user