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%
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