// // Conversation.swift // Zyquo Cloud // // Author: Simon-Pierre Boucher // Mail: contact@spboucher.ai // import Foundation /// A chat thread. Persisted as one JSON file per conversation in /// `~/Library/Application Support/ZyquoCloud/Conversations/`. struct Conversation: Codable, Identifiable, Hashable { let id: UUID var title: String var messages: [Message] /// Current model selection (changeable mid-conversation and per message). var modelID: String var provider: ProviderID var systemPrompt: String? var parameters: ChatParameters var personaID: UUID? var isPinned: Bool = false var folder: String? var tags: [String] = [] var createdAt: Date var updatedAt: Date /// True until the first exchange completes and a title is auto-generated. var hasAutoTitle: Bool = true init( id: UUID = UUID(), title: String = "New Chat", messages: [Message] = [], modelID: String, provider: ProviderID, systemPrompt: String? = nil, parameters: ChatParameters = ChatParameters(), personaID: UUID? = nil, createdAt: Date = Date(), updatedAt: Date = Date() ) { self.id = id self.title = title self.messages = messages self.modelID = modelID self.provider = provider self.systemPrompt = systemPrompt self.parameters = parameters self.personaID = personaID self.createdAt = createdAt self.updatedAt = updatedAt } /// Total usage across all assistant messages. var totalUsage: TokenUsage { messages.compactMap(\.usage).reduce(TokenUsage(), +) } /// Total estimated cost in USD. var totalCost: Double { messages.compactMap(\.estimatedCost).reduce(0, +) } } /// Per-conversation generation parameters. `nil` means "provider default" and /// the parameter is omitted from the request entirely. struct ChatParameters: Codable, Hashable { var temperature: Double? var topP: Double? var maxTokens: Int? var frequencyPenalty: Double? var presencePenalty: Double? /// "low" / "medium" / "high" for models supporting reasoning_effort. var reasoningEffort: String? /// Explicit thinking toggle for Anthropic/Qwen-style models. var thinkingEnabled: Bool? } /// A reusable configuration: system prompt + preferred model + parameters. struct Persona: Codable, Identifiable, Hashable { let id: UUID var name: String /// SF Symbol name for the persona glyph. var symbolName: String var systemPrompt: String var modelID: String? var provider: ProviderID? var parameters: ChatParameters init( id: UUID = UUID(), name: String, symbolName: String = "person.crop.circle", systemPrompt: String, modelID: String? = nil, provider: ProviderID? = nil, parameters: ChatParameters = ChatParameters() ) { self.id = id self.name = name self.symbolName = symbolName self.systemPrompt = systemPrompt self.modelID = modelID self.provider = provider self.parameters = parameters } } /// A prompt template from the built-in library or created by the user. /// `{{input}}` in the body is replaced with the user's text. struct PromptTemplate: Codable, Identifiable, Hashable { let id: UUID var title: String var category: String var body: String var isBuiltIn: Bool init(id: UUID = UUID(), title: String, category: String, body: String, isBuiltIn: Bool = false) { self.id = id self.title = title self.category = category self.body = body self.isBuiltIn = isBuiltIn } }