From 1dd801d98f2a483071ebbd8c8cb250fecaf8853b Mon Sep 17 00:00:00 2001 From: Logan Yang Date: Tue, 24 Jun 2025 23:01:58 -0700 Subject: [PATCH] feat: Update file cache to use markdown instead of json (#1572) --- src/cache/fileCache.ts | 49 +++++++++++++++++++++++++++++----- src/tools/FileParserManager.ts | 24 ++++++++++++++--- 2 files changed, 63 insertions(+), 10 deletions(-) diff --git a/src/cache/fileCache.ts b/src/cache/fileCache.ts index c2f48007..4d58bbe4 100644 --- a/src/cache/fileCache.ts +++ b/src/cache/fileCache.ts @@ -37,7 +37,7 @@ export class FileCache { } private getCachePath(cacheKey: string): string { - return `${this.cacheDir}/${cacheKey}.json`; + return `${this.cacheDir}/${cacheKey}.md`; } async get(cacheKey: string): Promise { @@ -53,7 +53,34 @@ export class FileCache { if (await app.vault.adapter.exists(cachePath)) { logInfo("File cache hit:", cacheKey); const cacheContent = await app.vault.adapter.read(cachePath); - const cacheEntry = JSON.parse(cacheContent) as FileCacheEntry; + + // .md files contain either plain string content or JSON-serialized content + // The safest approach is to go back to a simpler method that doesn't try to embed metadata in the content itself. + // Since preserving timestamps in file-based cache is not critical (memory cache handles active sessions) + let parsedContent: T; + + // Try to parse as JSON first (for non-string types that were serialized) + const trimmedContent = cacheContent.trim(); + if ( + (trimmedContent.startsWith("{") && trimmedContent.endsWith("}")) || + (trimmedContent.startsWith("[") && trimmedContent.endsWith("]")) + ) { + try { + parsedContent = JSON.parse(cacheContent); + } catch { + // JSON parsing failed, treat as string content + parsedContent = cacheContent as T; + } + } else { + // Plain text content (primary case for markdown) + parsedContent = cacheContent as T; + } + + // Create cache entry for memory storage (file-based cache doesn't preserve timestamps) + const cacheEntry: FileCacheEntry = { + content: parsedContent, + timestamp: Date.now(), + }; // Store in memory cache this.memoryCache.set(cacheKey, cacheEntry); @@ -74,16 +101,26 @@ export class FileCache { await this.ensureCacheDir(); const cachePath = this.getCachePath(cacheKey); + const timestamp = Date.now(); const cacheEntry: FileCacheEntry = { content, - timestamp: Date.now(), + timestamp, }; // Store in memory cache this.memoryCache.set(cacheKey, cacheEntry); - // Store in file cache - await app.vault.adapter.write(cachePath, JSON.stringify(cacheEntry)); + // Serialize content properly for file storage + let serializedContent: string; + if (typeof content === "string") { + // If content is already a string, use it directly + serializedContent = content; + } else { + // For non-string content, serialize as JSON + serializedContent = JSON.stringify(content, null, 2); + } + + await app.vault.adapter.write(cachePath, serializedContent); logInfo("Cached file content:", cacheKey); } catch (error) { logError("Error writing to file cache:", error); @@ -95,7 +132,7 @@ export class FileCache { // Remove from memory cache this.memoryCache.delete(cacheKey); - // Remove from file cache + // Remove from file cache (markdown format) const cachePath = this.getCachePath(cacheKey); if (await app.vault.adapter.exists(cachePath)) { await app.vault.adapter.remove(cachePath); diff --git a/src/tools/FileParserManager.ts b/src/tools/FileParserManager.ts index 4f759275..b54b5af0 100644 --- a/src/tools/FileParserManager.ts +++ b/src/tools/FileParserManager.ts @@ -229,18 +229,34 @@ export class Docs4LLMParser implements FileParser { throw new Error("Empty response from docs4llm API"); } - // Ensure response is a string + // Extract markdown content from response let content = ""; if (typeof docs4llmResponse.response === "string") { content = docs4llmResponse.response; + } else if (Array.isArray(docs4llmResponse.response)) { + // Handle array of documents from docs4llm + const markdownParts: string[] = []; + for (const doc of docs4llmResponse.response) { + if (doc.content) { + // Prioritize markdown content, then fallback to text content + if (doc.content.md) { + markdownParts.push(doc.content.md); + } else if (doc.content.text) { + markdownParts.push(doc.content.text); + } + } + } + content = markdownParts.join("\n\n"); } else if (typeof docs4llmResponse.response === "object") { - // If response is an object, try to get the text content - if (docs4llmResponse.response.text) { + // Handle single object response (backward compatibility) + if (docs4llmResponse.response.md) { + content = docs4llmResponse.response.md; + } else if (docs4llmResponse.response.text) { content = docs4llmResponse.response.text; } else if (docs4llmResponse.response.content) { content = docs4llmResponse.response.content; } else { - // If no text/content field, stringify the entire response + // If no markdown/text/content field, stringify the entire response content = JSON.stringify(docs4llmResponse.response, null, 2); } } else {