// // ModelRouter.swift // Prisme // // Author: Simon-Pierre Boucher // import Foundation import FoundationModels import Observation /// Effort level a task declares. The router decides what actually runs — /// no other code calls a model directly (CLAUDE.md §3). enum Tier { /// Pure heuristics, zero AI. The first choice whenever it suffices. case none /// On-device Foundation Models: free, unlimited, offline. case local /// Private Cloud Compute: explicit user action only, never on /// sensitive-container content. Not wired yet (P2). case cloud } /// Availability of the on-device model, with a user-presentable reason. /// AI is an enhancement: Prisme must remain a great browser without it — /// never an error screen, never a dead button (CLAUDE.md §2). enum LocalModelAvailability: Equatable { case available case unavailable(reason: String) } /// Single owner of model availability and session creation. @MainActor @Observable final class ModelRouter { private(set) var local: LocalModelAvailability = .unavailable(reason: "") /// Serializes on-device inference (Neural Engine runs requests in /// series; more than 2 in flight only builds a hidden queue). let queue = InferenceQueue(maxConcurrent: 2) init() { refreshAvailability() } var isLocalAvailable: Bool { local == .available } func refreshAvailability() { switch SystemLanguageModel.default.availability { case .available: local = .available case .unavailable(let reason): local = .unavailable(reason: Self.describe(reason)) } } private static func describe(_ reason: SystemLanguageModel.Availability.UnavailableReason) -> String { switch reason { case .deviceNotEligible: "Cet appareil ne prend pas en charge l'intelligence locale. Prisme reste un navigateur complet." case .appleIntelligenceNotEnabled: "Apple Intelligence est désactivé. Activez-le dans Réglages pour les résumés locaux." case .modelNotReady: "Le modèle local se prépare (téléchargement en cours). Réessayez dans quelques minutes." @unknown default: "L'intelligence locale n'est pas disponible pour le moment." } } }