Fix merge conflicts and apply all semantic cache fixes

This commit is contained in:
2026-05-07 22:36:09 +02:00
parent d9ef748b6f
commit c598d9cf01
4 changed files with 29 additions and 43 deletions
+1 -1
View File
@@ -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',
},
};
+5 -4
View File
@@ -170,12 +170,13 @@ 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;
text
.setValue(this.plugin.settings.cacheConfig.chromaURL || 'http://localhost:8000')
.onChange(async (value) => {
this.plugin.settings.cacheConfig.chromaURL = value;
await this.plugin.saveSettings();
this.plugin.notifyChatViews();
})
);
+20 -35
View File
@@ -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<ChromaClient['getOrCreateCollection']> | 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<void> {
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<void> {
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<number[]> {
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 }],
+1 -1
View File
@@ -95,7 +95,7 @@ export interface CacheConfig {
similarityThreshold: number;
collectionName: string;
embeddingModel: string;
chromaUrl: string;
chromaURL?: string;
}
export interface PluginSettings {