// // ProviderRegistry.swift // oAI // // Registry for managing multiple AI providers // // SPDX-License-Identifier: AGPL-3.0-or-later // Copyright (C) 2026 Rune Olsen // // This file is part of oAI. // // oAI is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // oAI is distributed in the hope that it will be useful, but WITHOUT // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General // Public License for more details. // // You should have received a copy of the GNU Affero General Public // License along with oAI. If not, see . import Foundation import os class ProviderRegistry { static let shared = ProviderRegistry() private var providers: [Settings.Provider: AIProvider] = [:] private let settings = SettingsService.shared private init() {} // MARK: - Get Provider func getProvider(for providerType: Settings.Provider) -> AIProvider? { // Return cached provider if exists if let provider = providers[providerType] { return provider } // Create new provider based on type let provider: AIProvider? switch providerType { case .openrouter: guard let apiKey = settings.openrouterAPIKey, !apiKey.isEmpty else { Log.api.warning("No API key configured for OpenRouter") return nil } provider = OpenRouterProvider(apiKey: apiKey) case .anthropic: guard let apiKey = settings.anthropicAPIKey, !apiKey.isEmpty else { Log.api.warning("No API key configured for Anthropic") return nil } provider = AnthropicProvider(apiKey: apiKey) case .openai: guard let apiKey = settings.openaiAPIKey, !apiKey.isEmpty else { Log.api.warning("No API key configured for OpenAI") return nil } provider = OpenAIProvider(apiKey: apiKey) case .ollama: provider = OllamaProvider(baseURL: settings.ollamaEffectiveURL) } // Cache and return if let provider = provider { Log.api.info("Created \(providerType.rawValue) provider") providers[providerType] = provider } return provider } // MARK: - Current Provider func getCurrentProvider() -> AIProvider? { let currentProviderType = settings.defaultProvider return getProvider(for: currentProviderType) } // MARK: - Clear Cache func clearCache() { providers.removeAll() } // MARK: - Validate API Key func hasValidAPIKey(for providerType: Settings.Provider) -> Bool { switch providerType { case .openrouter: return settings.openrouterAPIKey != nil && !settings.openrouterAPIKey!.isEmpty case .anthropic: return AnthropicOAuthService.shared.isAuthenticated || (settings.anthropicAPIKey != nil && !settings.anthropicAPIKey!.isEmpty) case .openai: return settings.openaiAPIKey != nil && !settings.openaiAPIKey!.isEmpty case .ollama: return settings.ollamaConfigured } } /// Providers that have credentials configured (API key or, for Ollama, a saved URL) var configuredProviders: [Settings.Provider] { Settings.Provider.allCases.filter { hasValidAPIKey(for: $0) } } }