|
import Dexie, { Table } from "dexie"; |
|
import { IConfig, DEFAULT_CONFIG, loadAllModels } from "./types"; |
|
|
|
export class ConfigManager extends Dexie { |
|
configs!: Table<IConfig>; |
|
private static instance: ConfigManager; |
|
private currentConfig: IConfig | null = null; |
|
private initPromise: Promise<IConfig> | null = null; |
|
|
|
private constructor() { |
|
super("configs"); |
|
this.version(1).stores({ |
|
configs: "id, createdAt, updatedAt", |
|
}); |
|
this.initPromise = this.initialize(); |
|
} |
|
|
|
public static getInstance(): ConfigManager { |
|
if (!ConfigManager.instance) { |
|
ConfigManager.instance = new ConfigManager(); |
|
} |
|
return ConfigManager.instance; |
|
} |
|
|
|
async initialize(): Promise<IConfig> { |
|
|
|
if (this.currentConfig) return this.currentConfig; |
|
if (this.initPromise) return this.initPromise; |
|
|
|
try { |
|
const configs = await this.configs.toArray(); |
|
|
|
if (configs.length === 0) { |
|
|
|
const defaultConfig: IConfig = { |
|
...DEFAULT_CONFIG, |
|
id: crypto.randomUUID(), |
|
createdAt: Date.now(), |
|
updatedAt: Date.now(), |
|
ollama_available: false |
|
}; |
|
|
|
|
|
if (defaultConfig.ollama_base_url.trim() !== '') { |
|
defaultConfig.ollama_available = await this.checkOllamaAvailability(defaultConfig.ollama_base_url); |
|
} |
|
|
|
await this.configs.add(defaultConfig); |
|
this.currentConfig = defaultConfig; |
|
} else { |
|
|
|
this.currentConfig = configs.sort((a, b) => b.updatedAt - a.updatedAt)[0]; |
|
|
|
|
|
if (this.currentConfig.ollama_base_url.trim() !== '') { |
|
const ollamaAvailable = await this.checkOllamaAvailability(this.currentConfig.ollama_base_url); |
|
|
|
|
|
if (ollamaAvailable !== this.currentConfig.ollama_available) { |
|
await this.updateConfig({ ollama_available: ollamaAvailable }); |
|
} |
|
} |
|
} |
|
|
|
|
|
await this.loadModels(); |
|
return this.currentConfig; |
|
} catch (error) { |
|
console.error("Error initializing config:", error); |
|
throw error; |
|
} finally { |
|
this.initPromise = null; |
|
} |
|
} |
|
|
|
private async checkOllamaAvailability(baseUrl: string): Promise<boolean> { |
|
if (!baseUrl || baseUrl.trim() === '') return false; |
|
|
|
try { |
|
const response = await fetch(`${baseUrl}/api/tags`); |
|
if (!response.ok) return false; |
|
|
|
const data = await response.json(); |
|
return Array.isArray(data.models) && data.models.length > 0; |
|
} catch (error) { |
|
console.error("Ollama server not available:", error); |
|
return false; |
|
} |
|
} |
|
|
|
private async loadModels(): Promise<void> { |
|
if (!this.currentConfig) return; |
|
|
|
try { |
|
const { ollamaAvailable } = await loadAllModels( |
|
this.currentConfig.ollama_base_url, |
|
this.currentConfig.openai_model, |
|
this.currentConfig.hf_custom_models |
|
); |
|
|
|
|
|
if (ollamaAvailable !== this.currentConfig.ollama_available) { |
|
await this.updateConfig({ ollama_available: ollamaAvailable }); |
|
} |
|
} catch (error) { |
|
console.error("Error loading models:", error); |
|
} |
|
} |
|
|
|
async getConfig(): Promise<IConfig> { |
|
return this.currentConfig || this.initialize(); |
|
} |
|
|
|
async updateConfig(updates: Partial<Omit<IConfig, 'id' | 'createdAt' | 'updatedAt'>>): Promise<IConfig> { |
|
const current = await this.getConfig(); |
|
const updatedConfig: IConfig = { |
|
...current, |
|
...updates, |
|
updatedAt: Date.now(), |
|
}; |
|
|
|
await this.configs.put(updatedConfig); |
|
this.currentConfig = updatedConfig; |
|
|
|
|
|
if (updates.ollama_base_url !== undefined && |
|
updates.ollama_base_url !== current.ollama_base_url) { |
|
|
|
|
|
const ollamaAvailable = updates.ollama_base_url.trim() === '' |
|
? false |
|
: await this.checkOllamaAvailability(updates.ollama_base_url); |
|
|
|
if (ollamaAvailable !== this.currentConfig.ollama_available) { |
|
return this.updateConfig({ ollama_available: ollamaAvailable }); |
|
} |
|
|
|
|
|
await this.loadModels(); |
|
} |
|
|
|
|
|
if (updates.openai_model !== undefined && |
|
updates.openai_model !== current.openai_model) { |
|
await this.loadModels(); |
|
} |
|
|
|
|
|
if (updates.hf_custom_models !== undefined && |
|
updates.hf_custom_models !== current.hf_custom_models) { |
|
await this.loadModels(); |
|
} |
|
|
|
return updatedConfig; |
|
} |
|
|
|
async resetToDefaults(): Promise<IConfig> { |
|
const current = await this.getConfig(); |
|
const resetConfig: IConfig = { |
|
...DEFAULT_CONFIG, |
|
id: current.id, |
|
createdAt: current.createdAt, |
|
updatedAt: Date.now(), |
|
ollama_available: false |
|
}; |
|
|
|
|
|
if (resetConfig.ollama_base_url.trim() !== '') { |
|
resetConfig.ollama_available = await this.checkOllamaAvailability(resetConfig.ollama_base_url); |
|
} |
|
|
|
await this.configs.put(resetConfig); |
|
this.currentConfig = resetConfig; |
|
|
|
|
|
await this.loadModels(); |
|
|
|
return resetConfig; |
|
} |
|
} |
|
|