SPB Git

spb/zyquo-cloud Public MIT

Native macOS AI chat client for 12 cloud providers — your keys, every cloud model, one beautiful chat.

Swift 97.4% Shell 1.7% Makefile 1%
3.7 KB · 127 lines swift
Raw Blame History
1//2//  Conversation.swift3//  Zyquo Cloud4//5//  Author: Simon-Pierre Boucher6//  Mail: contact@spboucher.ai7//89import Foundation1011/// A chat thread. Persisted as one JSON file per conversation in12/// `~/Library/Application Support/ZyquoCloud/Conversations/`.13struct Conversation: Codable, Identifiable, Hashable {14    let id: UUID15    var title: String16    var messages: [Message]17    /// Current model selection (changeable mid-conversation and per message).18    var modelID: String19    var provider: ProviderID20    var systemPrompt: String?21    var parameters: ChatParameters22    var personaID: UUID?23    var isPinned: Bool = false24    var folder: String?25    var tags: [String] = []26    var createdAt: Date27    var updatedAt: Date28    /// True until the first exchange completes and a title is auto-generated.29    var hasAutoTitle: Bool = true3031    init(32        id: UUID = UUID(),33        title: String = "New Chat",34        messages: [Message] = [],35        modelID: String,36        provider: ProviderID,37        systemPrompt: String? = nil,38        parameters: ChatParameters = ChatParameters(),39        personaID: UUID? = nil,40        createdAt: Date = Date(),41        updatedAt: Date = Date()42    ) {43        self.id = id44        self.title = title45        self.messages = messages46        self.modelID = modelID47        self.provider = provider48        self.systemPrompt = systemPrompt49        self.parameters = parameters50        self.personaID = personaID51        self.createdAt = createdAt52        self.updatedAt = updatedAt53    }5455    /// Total usage across all assistant messages.56    var totalUsage: TokenUsage {57        messages.compactMap(\.usage).reduce(TokenUsage(), +)58    }5960    /// Total estimated cost in USD.61    var totalCost: Double {62        messages.compactMap(\.estimatedCost).reduce(0, +)63    }64}6566/// Per-conversation generation parameters. `nil` means "provider default" and67/// the parameter is omitted from the request entirely.68struct ChatParameters: Codable, Hashable {69    var temperature: Double?70    var topP: Double?71    var maxTokens: Int?72    var frequencyPenalty: Double?73    var presencePenalty: Double?74    /// "low" / "medium" / "high" for models supporting reasoning_effort.75    var reasoningEffort: String?76    /// Explicit thinking toggle for Anthropic/Qwen-style models.77    var thinkingEnabled: Bool?78}7980/// A reusable configuration: system prompt + preferred model + parameters.81struct Persona: Codable, Identifiable, Hashable {82    let id: UUID83    var name: String84    /// SF Symbol name for the persona glyph.85    var symbolName: String86    var systemPrompt: String87    var modelID: String?88    var provider: ProviderID?89    var parameters: ChatParameters9091    init(92        id: UUID = UUID(),93        name: String,94        symbolName: String = "person.crop.circle",95        systemPrompt: String,96        modelID: String? = nil,97        provider: ProviderID? = nil,98        parameters: ChatParameters = ChatParameters()99    ) {100        self.id = id101        self.name = name102        self.symbolName = symbolName103        self.systemPrompt = systemPrompt104        self.modelID = modelID105        self.provider = provider106        self.parameters = parameters107    }108}109110/// A prompt template from the built-in library or created by the user.111/// `{{input}}` in the body is replaced with the user's text.112struct PromptTemplate: Codable, Identifiable, Hashable {113    let id: UUID114    var title: String115    var category: String116    var body: String117    var isBuiltIn: Bool118119    init(id: UUID = UUID(), title: String, category: String, body: String, isBuiltIn: Bool = false) {120        self.id = id121        self.title = title122        self.category = category123        self.body = body124        self.isBuiltIn = isBuiltIn125    }126}127