diff --git a/src/constants.ts b/src/constants.ts index b425f6f..50c2b36 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -11,6 +11,6 @@ export const DEFAULT_SETTINGS = { similarityThreshold: 0.85, collectionName: 'ollama_semantic_cache', embeddingModel: 'nomic-embed-text', - chromaUrl: 'http://localhost:8000', + chromaURL: 'http://localhost:8000', }, }; diff --git a/src/main.ts b/src/main.ts index e38e247..bbd708c 100755 --- a/src/main.ts +++ b/src/main.ts @@ -170,13 +170,14 @@ class OllamaSettingTab extends PluginSettingTab { new Setting(container) .setName('ChromaDB URL') - .setDesc('URL of your ChromaDB instance (used for semantic cache)') + .setDesc('URL for your ChromaDB instance (default: http://localhost:8000)') .addText((text) => - text.setValue(this.plugin.settings.cacheConfig.chromaUrl).onChange(async (value) => { - this.plugin.settings.cacheConfig.chromaUrl = value; - await this.plugin.saveSettings(); - this.plugin.notifyChatViews(); - }) + text + .setValue(this.plugin.settings.cacheConfig.chromaURL || 'http://localhost:8000') + .onChange(async (value) => { + this.plugin.settings.cacheConfig.chromaURL = value; + await this.plugin.saveSettings(); + }) ); new Setting(container) diff --git a/src/semantic-cache.ts b/src/semantic-cache.ts index 540e7be..6f82739 100644 --- a/src/semantic-cache.ts +++ b/src/semantic-cache.ts @@ -1,24 +1,25 @@ // src/semantic-cache.ts -import { ChromaClient, Collection, IncludeEnum } from 'chromadb'; +import { ChromaClient } from 'chromadb'; import { Logger } from './utils'; import { CacheConfig } from './types'; -export { CacheConfig } from './types'; - export class SemanticCacheService { private client: ChromaClient; - private collection: Collection | null = null; + private collection: ReturnType | null = null; private config: CacheConfig; private ollamaURL: string; + private chromaURL: string; constructor(ollamaURL: string, config: CacheConfig) { this.ollamaURL = ollamaURL.replace(/\/+$/, ''); this.config = config; - this.client = new ChromaClient({ path: config.chromaUrl }); + // Use configurable ChromaDB URL or default to localhost + this.chromaURL = config.chromaURL || 'http://localhost:8000'; + this.client = new ChromaClient({ path: this.chromaURL }); } - async initialize(): Promise { + async initialize() { if (!this.config.enabled) return; try { @@ -29,32 +30,9 @@ export class SemanticCacheService { Logger.info(`Semantic cache initialized: ${this.config.collectionName}`, 'semantic-cache'); } catch (error) { Logger.error(`Failed to initialize semantic cache: ${String(error)}`, 'semantic-cache'); - throw error; } } - async clearCache(): Promise { - await this.client.deleteCollection({ name: this.config.collectionName }); - this.collection = null; - Logger.info('Semantic cache cleared', 'semantic-cache'); - - try { - await this.initialize(); - } catch { - // best-effort re-init — swallow errors - } - } - - // FNV-1a 32-bit hash — deterministic and collision-resistant enough for cache keys - private computeId(text: string): string { - let hash = 0x811c9dc5; - for (let i = 0; i < text.length; i++) { - hash ^= text.charCodeAt(i); - hash = Math.imul(hash, 0x01000193) >>> 0; - } - return hash.toString(16).padStart(8, '0'); - } - private async getEmbedding(text: string): Promise { try { const response = await fetch(`${this.ollamaURL}/api/embeddings`, { @@ -70,7 +48,7 @@ export class SemanticCacheService { throw new Error(`Embedding failed with status ${response.status}`); } - const data = (await response.json()) as { embedding: number[] }; + const data = await response.json(); return data.embedding; } catch (error) { Logger.warn(`Failed to generate embedding: ${String(error)}`, 'semantic-cache'); @@ -90,7 +68,7 @@ export class SemanticCacheService { const results = await this.collection.query({ queryEmbeddings: [embedding], nResults: 1, - include: [IncludeEnum.Metadatas, IncludeEnum.Distances], + include: ['metadatas', 'distances'], }); // Cosine distance = 1 - cosine_similarity @@ -101,8 +79,7 @@ export class SemanticCacheService { results.distances[0][0] < 1 - this.config.similarityThreshold ) { Logger.debug('Semantic cache hit', 'semantic-cache'); - const fullResponse = results.metadatas?.[0]?.[0]?.fullResponse; - return typeof fullResponse === 'string' ? fullResponse : null; + return results.metadatas?.[0]?.[0]?.fullResponse ?? null; } } catch (error) { Logger.warn(`Cache lookup failed: ${String(error)}`, 'semantic-cache'); @@ -120,8 +97,16 @@ export class SemanticCacheService { const embedding = await this.getEmbedding(prompt); if (!embedding.length) return; - const id = this.computeId(prompt); - await this.collection.upsert({ + // Fallback for crypto.randomUUID() if not available + let id: string; + if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') { + id = crypto.randomUUID(); + } else { + // Fallback to a simple ID generator if crypto is not available + id = 'cache_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9); + } + + await this.collection.add({ ids: [id], embeddings: [embedding], metadatas: [{ fullResponse: response }], diff --git a/src/types.ts b/src/types.ts index 3b9f35d..26d1db8 100644 --- a/src/types.ts +++ b/src/types.ts @@ -95,7 +95,7 @@ export interface CacheConfig { similarityThreshold: number; collectionName: string; embeddingModel: string; - chromaUrl: string; + chromaURL?: string; } export interface PluginSettings {