mirror of
				https://github.com/TriliumNext/Notes.git
				synced 2025-10-31 21:11:30 +08:00 
			
		
		
		
	fix(llm): changing providers works now
This commit is contained in:
		
							parent
							
								
									414781936b
								
							
						
					
					
						commit
						c6062f453a
					
				| @ -40,8 +40,8 @@ interface NoteContext { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export class AIServiceManager implements IAIServiceManager { | export class AIServiceManager implements IAIServiceManager { | ||||||
|     private services: Partial<Record<ServiceProviders, AIService>> = {}; |     private currentService: AIService | null = null; | ||||||
| 
 |     private currentProvider: ServiceProviders | null = null; | ||||||
|     private initialized = false; |     private initialized = false; | ||||||
| 
 | 
 | ||||||
|     constructor() { |     constructor() { | ||||||
| @ -50,8 +50,7 @@ export class AIServiceManager implements IAIServiceManager { | |||||||
|             log.error(`Error initializing LLM tools during AIServiceManager construction: ${error.message || String(error)}`); |             log.error(`Error initializing LLM tools during AIServiceManager construction: ${error.message || String(error)}`); | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|         // Set up event listener for provider changes
 |         // Removed complex provider change listener - we'll read options fresh each time
 | ||||||
|         this.setupProviderChangeListener(); |  | ||||||
| 
 | 
 | ||||||
|         this.initialized = true; |         this.initialized = true; | ||||||
|     } |     } | ||||||
| @ -174,40 +173,34 @@ export class AIServiceManager implements IAIServiceManager { | |||||||
|     /** |     /** | ||||||
|      * Get list of available providers |      * Get list of available providers | ||||||
|      */ |      */ | ||||||
|     getAvailableProviders(): ServiceProviders[] { |         getAvailableProviders(): ServiceProviders[] { | ||||||
|         this.ensureInitialized(); |         this.ensureInitialized(); | ||||||
| 
 | 
 | ||||||
|         const allProviders: ServiceProviders[] = ['openai', 'anthropic', 'ollama']; |         const allProviders: ServiceProviders[] = ['openai', 'anthropic', 'ollama']; | ||||||
|         const availableProviders: ServiceProviders[] = []; |         const availableProviders: ServiceProviders[] = []; | ||||||
| 
 | 
 | ||||||
|         for (const providerName of allProviders) { |         for (const providerName of allProviders) { | ||||||
|             // Use a sync approach - check if we can create the provider
 |             // Check configuration to see if provider would be available
 | ||||||
|             const service = this.services[providerName]; |             try { | ||||||
|             if (service && service.isAvailable()) { |                 switch (providerName) { | ||||||
|                 availableProviders.push(providerName); |                     case 'openai': | ||||||
|             } else { |                         if (options.getOption('openaiApiKey') || options.getOption('openaiBaseUrl')) { | ||||||
|                 // For providers not yet created, check configuration to see if they would be available
 |                             availableProviders.push(providerName); | ||||||
|                 try { |                         } | ||||||
|                     switch (providerName) { |                         break; | ||||||
|                         case 'openai': |                     case 'anthropic': | ||||||
|                             if (options.getOption('openaiApiKey')) { |                         if (options.getOption('anthropicApiKey')) { | ||||||
|                                 availableProviders.push(providerName); |                             availableProviders.push(providerName); | ||||||
|                             } |                         } | ||||||
|                             break; |                         break; | ||||||
|                         case 'anthropic': |                     case 'ollama': | ||||||
|                             if (options.getOption('anthropicApiKey')) { |                         if (options.getOption('ollamaBaseUrl')) { | ||||||
|                                 availableProviders.push(providerName); |                             availableProviders.push(providerName); | ||||||
|                             } |                         } | ||||||
|                             break; |                         break; | ||||||
|                         case 'ollama': |  | ||||||
|                             if (options.getOption('ollamaBaseUrl')) { |  | ||||||
|                                 availableProviders.push(providerName); |  | ||||||
|                             } |  | ||||||
|                             break; |  | ||||||
|                     } |  | ||||||
|                 } catch (error) { |  | ||||||
|                     // Ignore configuration errors, provider just won't be available
 |  | ||||||
|                 } |                 } | ||||||
|  |             } catch (error) { | ||||||
|  |                 // Ignore configuration errors, provider just won't be available
 | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @ -379,15 +372,37 @@ export class AIServiceManager implements IAIServiceManager { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Get or create a chat provider on-demand with inline validation |      * Clear the current provider (forces recreation on next access) | ||||||
|  |      */ | ||||||
|  |     public clearCurrentProvider(): void { | ||||||
|  |         this.currentService = null; | ||||||
|  |         this.currentProvider = null; | ||||||
|  |         log.info('Cleared current provider - will be recreated on next access'); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get or create the current provider instance - only one instance total | ||||||
|      */ |      */ | ||||||
|     private async getOrCreateChatProvider(providerName: ServiceProviders): Promise<AIService | null> { |     private async getOrCreateChatProvider(providerName: ServiceProviders): Promise<AIService | null> { | ||||||
|         // Return existing provider if already created
 |         // If provider type changed, clear the old one
 | ||||||
|         if (this.services[providerName]) { |         if (this.currentProvider && this.currentProvider !== providerName) { | ||||||
|             return this.services[providerName]; |             log.info(`Provider changed from ${this.currentProvider} to ${providerName}, clearing old service`); | ||||||
|  |             this.currentService = null; | ||||||
|  |             this.currentProvider = null; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // Create and validate provider on-demand
 |         // Return existing service if it matches and is available
 | ||||||
|  |         if (this.currentService && this.currentProvider === providerName && this.currentService.isAvailable()) { | ||||||
|  |             return this.currentService; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Clear invalid service
 | ||||||
|  |         if (this.currentService) { | ||||||
|  |             this.currentService = null; | ||||||
|  |             this.currentProvider = null; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Create new service for the requested provider
 | ||||||
|         try { |         try { | ||||||
|             let service: AIService | null = null; |             let service: AIService | null = null; | ||||||
| 
 | 
 | ||||||
| @ -398,7 +413,6 @@ export class AIServiceManager implements IAIServiceManager { | |||||||
|                     if (!apiKey && !baseUrl) return null; |                     if (!apiKey && !baseUrl) return null; | ||||||
| 
 | 
 | ||||||
|                     service = new OpenAIService(); |                     service = new OpenAIService(); | ||||||
|                     // Validate by checking if it's available
 |  | ||||||
|                     if (!service.isAvailable()) { |                     if (!service.isAvailable()) { | ||||||
|                         throw new Error('OpenAI service not available'); |                         throw new Error('OpenAI service not available'); | ||||||
|                     } |                     } | ||||||
| @ -429,7 +443,10 @@ export class AIServiceManager implements IAIServiceManager { | |||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             if (service) { |             if (service) { | ||||||
|                 this.services[providerName] = service; |                 // Cache the new service
 | ||||||
|  |                 this.currentService = service; | ||||||
|  |                 this.currentProvider = providerName; | ||||||
|  |                 log.info(`Created and cached new ${providerName} service`); | ||||||
|                 return service; |                 return service; | ||||||
|             } |             } | ||||||
|         } catch (error: any) { |         } catch (error: any) { | ||||||
| @ -630,28 +647,47 @@ export class AIServiceManager implements IAIServiceManager { | |||||||
|      * Check if a specific provider is available |      * Check if a specific provider is available | ||||||
|      */ |      */ | ||||||
|     isProviderAvailable(provider: string): boolean { |     isProviderAvailable(provider: string): boolean { | ||||||
|         return this.services[provider as ServiceProviders]?.isAvailable() ?? false; |         // Check if this is the current provider and if it's available
 | ||||||
|  |         if (this.currentProvider === provider && this.currentService) { | ||||||
|  |             return this.currentService.isAvailable(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // For other providers, check configuration
 | ||||||
|  |         try { | ||||||
|  |             switch (provider) { | ||||||
|  |                 case 'openai': | ||||||
|  |                     return !!(options.getOption('openaiApiKey') || options.getOption('openaiBaseUrl')); | ||||||
|  |                 case 'anthropic': | ||||||
|  |                     return !!options.getOption('anthropicApiKey'); | ||||||
|  |                 case 'ollama': | ||||||
|  |                     return !!options.getOption('ollamaBaseUrl'); | ||||||
|  |                 default: | ||||||
|  |                     return false; | ||||||
|  |             } | ||||||
|  |         } catch { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Get metadata about a provider |      * Get metadata about a provider | ||||||
|      */ |      */ | ||||||
|     getProviderMetadata(provider: string): ProviderMetadata | null { |     getProviderMetadata(provider: string): ProviderMetadata | null { | ||||||
|         const service = this.services[provider as ServiceProviders]; |         // Only return metadata if this is the current active provider
 | ||||||
|         if (!service) { |         if (this.currentProvider === provider && this.currentService) { | ||||||
|             return null; |             return { | ||||||
|  |                 name: provider, | ||||||
|  |                 capabilities: { | ||||||
|  |                     chat: true, | ||||||
|  |                     streaming: true, | ||||||
|  |                     functionCalling: provider === 'openai' // Only OpenAI has function calling
 | ||||||
|  |                 }, | ||||||
|  |                 models: ['default'], // Placeholder, could be populated from the service
 | ||||||
|  |                 defaultModel: 'default' | ||||||
|  |             }; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         return { |         return null; | ||||||
|             name: provider, |  | ||||||
|             capabilities: { |  | ||||||
|                 chat: true, |  | ||||||
|                 streaming: true, |  | ||||||
|                 functionCalling: provider === 'openai' // Only OpenAI has function calling
 |  | ||||||
|             }, |  | ||||||
|             models: ['default'], // Placeholder, could be populated from the service
 |  | ||||||
|             defaultModel: 'default' |  | ||||||
|         }; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -665,67 +701,8 @@ export class AIServiceManager implements IAIServiceManager { | |||||||
|         return String(error); |         return String(error); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     // Removed complex event listener and cache invalidation logic
 | ||||||
|      * Set up event listener for provider changes |     // Services will be created fresh when needed by reading current options
 | ||||||
|      */ |  | ||||||
|     private setupProviderChangeListener(): void { |  | ||||||
|         // List of AI-related options that should trigger service recreation
 |  | ||||||
|         const aiRelatedOptions = [ |  | ||||||
|             'aiEnabled', |  | ||||||
|             'aiSelectedProvider', |  | ||||||
|             'openaiApiKey', |  | ||||||
|             'openaiBaseUrl',  |  | ||||||
|             'openaiDefaultModel', |  | ||||||
|             'anthropicApiKey', |  | ||||||
|             'anthropicBaseUrl', |  | ||||||
|             'anthropicDefaultModel', |  | ||||||
|             'ollamaBaseUrl', |  | ||||||
|             'ollamaDefaultModel' |  | ||||||
|         ]; |  | ||||||
| 
 |  | ||||||
|         eventService.subscribe(['entityChanged'], async ({ entityName, entity }) => { |  | ||||||
|             if (entityName === 'options' && entity && aiRelatedOptions.includes(entity.name)) { |  | ||||||
|                 log.info(`AI-related option '${entity.name}' changed, recreating LLM services`); |  | ||||||
|                  |  | ||||||
|                 // Special handling for aiEnabled toggle
 |  | ||||||
|                 if (entity.name === 'aiEnabled') { |  | ||||||
|                     const isEnabled = entity.value === 'true'; |  | ||||||
|                      |  | ||||||
|                     if (isEnabled) { |  | ||||||
|                         log.info('AI features enabled, initializing AI service'); |  | ||||||
|                         // Initialize the AI service
 |  | ||||||
|                         await this.initialize(); |  | ||||||
|                     } else { |  | ||||||
|                         log.info('AI features disabled, clearing providers'); |  | ||||||
|                         // Clear chat providers
 |  | ||||||
|                         this.services = {}; |  | ||||||
|                     } |  | ||||||
|                 } else { |  | ||||||
|                     // For other AI-related options, recreate services on-demand
 |  | ||||||
|                     await this.recreateServices(); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Recreate LLM services when provider settings change |  | ||||||
|      */ |  | ||||||
|     private async recreateServices(): Promise<void> { |  | ||||||
|         try { |  | ||||||
|             log.info('Recreating LLM services due to configuration change'); |  | ||||||
| 
 |  | ||||||
|             // Clear configuration cache first
 |  | ||||||
|             clearConfigurationCache(); |  | ||||||
| 
 |  | ||||||
|             // Clear existing chat providers (they will be recreated on-demand)
 |  | ||||||
|             this.services = {}; |  | ||||||
| 
 |  | ||||||
|             log.info('LLM services recreated successfully'); |  | ||||||
|         } catch (error) { |  | ||||||
|             log.error(`Error recreating LLM services: ${this.handleError(error)}`); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,4 +1,3 @@ | |||||||
| import configurationManager from './configuration_manager.js'; |  | ||||||
| import optionService from '../../options.js'; | import optionService from '../../options.js'; | ||||||
| import log from '../../log.js'; | import log from '../../log.js'; | ||||||
| import type { | import type { | ||||||
| @ -13,7 +12,7 @@ import type { | |||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Get the selected AI provider |  * Get the selected AI provider - always fresh from options | ||||||
|  */ |  */ | ||||||
| export async function getSelectedProvider(): Promise<ProviderType | null> { | export async function getSelectedProvider(): Promise<ProviderType | null> { | ||||||
|     const providerOption = optionService.getOption('aiSelectedProvider'); |     const providerOption = optionService.getOption('aiSelectedProvider'); | ||||||
| @ -25,38 +24,100 @@ export async function getSelectedProvider(): Promise<ProviderType | null> { | |||||||
|  * Parse a model identifier (handles "provider:model" format) |  * Parse a model identifier (handles "provider:model" format) | ||||||
|  */ |  */ | ||||||
| export function parseModelIdentifier(modelString: string): ModelIdentifier { | export function parseModelIdentifier(modelString: string): ModelIdentifier { | ||||||
|     return configurationManager.parseModelIdentifier(modelString); |     if (!modelString) { | ||||||
|  |         return { | ||||||
|  |             modelId: '', | ||||||
|  |             fullIdentifier: '' | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const parts = modelString.split(':'); | ||||||
|  | 
 | ||||||
|  |     if (parts.length === 1) { | ||||||
|  |         // No provider prefix, just model name
 | ||||||
|  |         return { | ||||||
|  |             modelId: modelString, | ||||||
|  |             fullIdentifier: modelString | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Check if first part is a known provider
 | ||||||
|  |     const potentialProvider = parts[0].toLowerCase(); | ||||||
|  |     const knownProviders: ProviderType[] = ['openai', 'anthropic', 'ollama']; | ||||||
|  | 
 | ||||||
|  |     if (knownProviders.includes(potentialProvider as ProviderType)) { | ||||||
|  |         // Provider prefix format
 | ||||||
|  |         const provider = potentialProvider as ProviderType; | ||||||
|  |         const modelId = parts.slice(1).join(':'); // Rejoin in case model has colons
 | ||||||
|  | 
 | ||||||
|  |         return { | ||||||
|  |             provider, | ||||||
|  |             modelId, | ||||||
|  |             fullIdentifier: modelString | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Not a provider prefix, treat whole string as model name
 | ||||||
|  |     return { | ||||||
|  |         modelId: modelString, | ||||||
|  |         fullIdentifier: modelString | ||||||
|  |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Create a model configuration from a model string |  * Create a model configuration from a model string | ||||||
|  */ |  */ | ||||||
| export function createModelConfig(modelString: string, defaultProvider?: ProviderType): ModelConfig { | export function createModelConfig(modelString: string, defaultProvider?: ProviderType): ModelConfig { | ||||||
|     return configurationManager.createModelConfig(modelString, defaultProvider); |     const identifier = parseModelIdentifier(modelString); | ||||||
|  |     const provider = identifier.provider || defaultProvider || 'openai'; // fallback to openai if no provider specified
 | ||||||
|  | 
 | ||||||
|  |     return { | ||||||
|  |         provider, | ||||||
|  |         modelId: identifier.modelId, | ||||||
|  |         displayName: identifier.fullIdentifier | ||||||
|  |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Get the default model for a specific provider |  * Get the default model for a specific provider - always fresh from options | ||||||
|  */ |  */ | ||||||
| export async function getDefaultModelForProvider(provider: ProviderType): Promise<string | undefined> { | export async function getDefaultModelForProvider(provider: ProviderType): Promise<string | undefined> { | ||||||
|     const config = await configurationManager.getAIConfig(); |     const optionKey = `${provider}DefaultModel` as const; | ||||||
|     return config.defaultModels[provider]; // This can now be undefined
 |     return optionService.getOption(optionKey) || undefined; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Get provider settings for a specific provider |  * Get provider settings for a specific provider - always fresh from options | ||||||
|  */ |  */ | ||||||
| export async function getProviderSettings(provider: ProviderType) { | export async function getProviderSettings(provider: ProviderType) { | ||||||
|     const config = await configurationManager.getAIConfig(); |     switch (provider) { | ||||||
|     return config.providerSettings[provider]; |         case 'openai': | ||||||
|  |             return { | ||||||
|  |                 apiKey: optionService.getOption('openaiApiKey'), | ||||||
|  |                 baseUrl: optionService.getOption('openaiBaseUrl'), | ||||||
|  |                 defaultModel: optionService.getOption('openaiDefaultModel') | ||||||
|  |             }; | ||||||
|  |         case 'anthropic': | ||||||
|  |             return { | ||||||
|  |                 apiKey: optionService.getOption('anthropicApiKey'), | ||||||
|  |                 baseUrl: optionService.getOption('anthropicBaseUrl'), | ||||||
|  |                 defaultModel: optionService.getOption('anthropicDefaultModel') | ||||||
|  |             }; | ||||||
|  |         case 'ollama': | ||||||
|  |             return { | ||||||
|  |                 baseUrl: optionService.getOption('ollamaBaseUrl'), | ||||||
|  |                 defaultModel: optionService.getOption('ollamaDefaultModel') | ||||||
|  |             }; | ||||||
|  |         default: | ||||||
|  |             return {}; | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Check if AI is enabled |  * Check if AI is enabled - always fresh from options | ||||||
|  */ |  */ | ||||||
| export async function isAIEnabled(): Promise<boolean> { | export async function isAIEnabled(): Promise<boolean> { | ||||||
|     const config = await configurationManager.getAIConfig(); |     return optionService.getOptionBool('aiEnabled'); | ||||||
|     return config.enabled; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
| @ -95,17 +156,51 @@ export async function getAvailableSelectedProvider(): Promise<ProviderType | nul | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Validate the current AI configuration |  * Validate the current AI configuration - simplified validation | ||||||
|  */ |  */ | ||||||
| export async function validateConfiguration() { | export async function validateConfiguration() { | ||||||
|     return configurationManager.validateConfig(); |     const result = { | ||||||
|  |         isValid: true, | ||||||
|  |         errors: [] as string[], | ||||||
|  |         warnings: [] as string[] | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     const aiEnabled = await isAIEnabled(); | ||||||
|  |     if (!aiEnabled) { | ||||||
|  |         result.warnings.push('AI features are disabled'); | ||||||
|  |         return result; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const selectedProvider = await getSelectedProvider(); | ||||||
|  |     if (!selectedProvider) { | ||||||
|  |         result.errors.push('No AI provider selected'); | ||||||
|  |         result.isValid = false; | ||||||
|  |         return result; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Validate provider-specific settings
 | ||||||
|  |     const settings = await getProviderSettings(selectedProvider); | ||||||
|  | 
 | ||||||
|  |     if (selectedProvider === 'openai' && !(settings as any)?.apiKey) { | ||||||
|  |         result.warnings.push('OpenAI API key is not configured'); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (selectedProvider === 'anthropic' && !(settings as any)?.apiKey) { | ||||||
|  |         result.warnings.push('Anthropic API key is not configured'); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (selectedProvider === 'ollama' && !(settings as any)?.baseUrl) { | ||||||
|  |         result.warnings.push('Ollama base URL is not configured'); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Clear cached configuration (use when settings change) |  * Clear cached configuration (no-op since we removed caching) | ||||||
|  */ |  */ | ||||||
| export function clearConfigurationCache(): void { | export function clearConfigurationCache(): void { | ||||||
|     configurationManager.clearCache(); |     // No caching anymore, so nothing to clear
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  | |||||||
| @ -21,11 +21,6 @@ import type { | |||||||
|  */ |  */ | ||||||
| export class ConfigurationManager { | export class ConfigurationManager { | ||||||
|     private static instance: ConfigurationManager | null = null; |     private static instance: ConfigurationManager | null = null; | ||||||
|     private cachedConfig: AIConfig | null = null; |  | ||||||
|     private lastConfigUpdate: number = 0; |  | ||||||
| 
 |  | ||||||
|     // Cache for 5 minutes to avoid excessive option reads
 |  | ||||||
|     private static readonly CACHE_DURATION = 5 * 60 * 1000; |  | ||||||
| 
 | 
 | ||||||
|     private constructor() {} |     private constructor() {} | ||||||
| 
 | 
 | ||||||
| @ -37,14 +32,9 @@ export class ConfigurationManager { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Get the complete AI configuration |      * Get the complete AI configuration - always fresh, no caching | ||||||
|      */ |      */ | ||||||
|     public async getAIConfig(): Promise<AIConfig> { |     public async getAIConfig(): Promise<AIConfig> { | ||||||
|         const now = Date.now(); |  | ||||||
|         if (this.cachedConfig && (now - this.lastConfigUpdate) < ConfigurationManager.CACHE_DURATION) { |  | ||||||
|             return this.cachedConfig; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         try { |         try { | ||||||
|             const config: AIConfig = { |             const config: AIConfig = { | ||||||
|                 enabled: await this.getAIEnabled(), |                 enabled: await this.getAIEnabled(), | ||||||
| @ -53,8 +43,6 @@ export class ConfigurationManager { | |||||||
|                 providerSettings: await this.getProviderSettings() |                 providerSettings: await this.getProviderSettings() | ||||||
|             }; |             }; | ||||||
| 
 | 
 | ||||||
|             this.cachedConfig = config; |  | ||||||
|             this.lastConfigUpdate = now; |  | ||||||
|             return config; |             return config; | ||||||
|         } catch (error) { |         } catch (error) { | ||||||
|             log.error(`Error loading AI configuration: ${error}`); |             log.error(`Error loading AI configuration: ${error}`); | ||||||
| @ -263,14 +251,6 @@ export class ConfigurationManager { | |||||||
|         return result; |         return result; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |  | ||||||
|      * Clear cached configuration (force reload on next access) |  | ||||||
|      */ |  | ||||||
|     public clearCache(): void { |  | ||||||
|         this.cachedConfig = null; |  | ||||||
|         this.lastConfigUpdate = 0; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // Private helper methods
 |     // Private helper methods
 | ||||||
| 
 | 
 | ||||||
|     private async getAIEnabled(): Promise<boolean> { |     private async getAIEnabled(): Promise<boolean> { | ||||||
|  | |||||||
| @ -82,6 +82,26 @@ function setOption<T extends OptionNames>(name: T, value: string | OptionDefinit | |||||||
|     } else { |     } else { | ||||||
|         createOption(name, value, false); |         createOption(name, value, false); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     // Clear current AI provider when AI-related options change
 | ||||||
|  |     const aiOptions = [ | ||||||
|  |         'aiSelectedProvider', 'openaiApiKey', 'openaiBaseUrl', 'openaiDefaultModel', | ||||||
|  |         'anthropicApiKey', 'anthropicBaseUrl', 'anthropicDefaultModel', | ||||||
|  |         'ollamaBaseUrl', 'ollamaDefaultModel' | ||||||
|  |     ]; | ||||||
|  | 
 | ||||||
|  |     if (aiOptions.includes(name)) { | ||||||
|  |         // Import dynamically to avoid circular dependencies
 | ||||||
|  |         setImmediate(async () => { | ||||||
|  |             try { | ||||||
|  |                 const aiServiceManager = (await import('./llm/ai_service_manager.js')).default; | ||||||
|  |                 aiServiceManager.getInstance().clearCurrentProvider(); | ||||||
|  |                 console.log(`Cleared AI provider after ${name} option changed`); | ||||||
|  |             } catch (error) { | ||||||
|  |                 console.log(`Could not clear AI provider: ${error}`); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 perf3ct
						perf3ct