SPB Git

spb/zyquo-local Public MIT

Native macOS AI chat that runs LLMs 100% locally on Apple Silicon with MLX — no cloud, no API keys.

Swift 97.2% Shell 1.8% Makefile 1%
3.0 KB · 102 lines swift
Raw Blame History
1//2//  AppSettings.swift3//  Zyquo Local4//5//  Author: Simon-Pierre Boucher6//  Mail: contact@spboucher.ai7//89import Foundation10import Observation1112/// App-wide settings, persisted as JSON. Appearance lives in ThemeStore.13@MainActor14@Observable15final class AppSettings {16    /// Model loaded automatically on launch (repo ID), nil = none.17    var defaultModelID: String? {18        didSet { persist() }19    }2021    /// Keep the model loaded when the window closes / app is in background.22    var keepModelLoaded: Bool {23        didSet { persist() }24    }2526    /// Hugging Face token for gated models. Sent only to huggingface.co.27    var hfToken: String {28        didSet { persist() }29    }3031    /// Verify file sizes after download completes.32    var autoVerifyDownloads: Bool {33        didSet { persist() }34    }3536    /// Default generation parameters for new conversations.37    var defaultParams: GenerationParams {38        didSet { persist() }39    }4041    /// MLX GPU buffer-cache limit in MB (0 = MLX default).42    var gpuCacheLimitMB: Int {43        didSet { persist() }44    }4546    /// Hard cap applied on top of the model's context window (0 = model max).47    var contextLengthCap: Int {48        didSet { persist() }49    }5051    /// Global default system prompt for new conversations.52    var defaultSystemPrompt: String {53        didSet { persist() }54    }5556    var menuBarExtraEnabled: Bool {57        didSet { persist() }58    }5960    private struct Persisted: Codable {61        var defaultModelID: String?62        var keepModelLoaded: Bool63        var hfToken: String64        var autoVerifyDownloads: Bool65        var defaultParams: GenerationParams66        var gpuCacheLimitMB: Int67        var contextLengthCap: Int68        var defaultSystemPrompt: String69        var menuBarExtraEnabled: Bool70    }7172    init() {73        let stored = PersistenceService.loadDocument(Persisted.self, named: "settings")74        defaultModelID = stored?.defaultModelID75        keepModelLoaded = stored?.keepModelLoaded ?? true76        hfToken = stored?.hfToken ?? ""77        autoVerifyDownloads = stored?.autoVerifyDownloads ?? true78        defaultParams = stored?.defaultParams ?? GenerationParams()79        gpuCacheLimitMB = stored?.gpuCacheLimitMB ?? 080        contextLengthCap = stored?.contextLengthCap ?? 081        defaultSystemPrompt = stored?.defaultSystemPrompt ?? ""82        menuBarExtraEnabled = stored?.menuBarExtraEnabled ?? true83    }8485    private func persist() {86        PersistenceService.saveDocument(87            Persisted(88                defaultModelID: defaultModelID,89                keepModelLoaded: keepModelLoaded,90                hfToken: hfToken,91                autoVerifyDownloads: autoVerifyDownloads,92                defaultParams: defaultParams,93                gpuCacheLimitMB: gpuCacheLimitMB,94                contextLengthCap: contextLengthCap,95                defaultSystemPrompt: defaultSystemPrompt,96                menuBarExtraEnabled: menuBarExtraEnabled97            ),98            named: "settings"99        )100    }101}102