// // Message.swift // Zyquo Atlas // // Author: Simon-Pierre Boucher // Mail: contact@spboucher.ai // import Foundation /// One turn in a conversation transcript. struct Message: Codable, Identifiable, Hashable { enum Role: String, Codable { case system case user case assistant } let id: UUID var role: Role var text: String /// Reasoning/thinking text streamed by reasoning models (collapsible in the UI). var reasoning: String? /// Image attachments (user messages, vision models). var attachments: [Attachment] /// Web-search citations (Perplexity). var citations: [Citation] /// Model that produced this message (assistant turns) or was targeted (user turns). var modelID: String? var provider: ProviderID? var usage: TokenUsage? /// Estimated USD cost computed from catalog pricing at receive time. var estimatedCost: Double? var createdAt: Date /// Set while a response is streaming; exactly one message can be streaming at a time. var isStreaming: Bool = false /// Human-readable error if generation failed mid-message. var errorText: String? init( id: UUID = UUID(), role: Role, text: String, reasoning: String? = nil, attachments: [Attachment] = [], citations: [Citation] = [], modelID: String? = nil, provider: ProviderID? = nil, usage: TokenUsage? = nil, estimatedCost: Double? = nil, createdAt: Date = Date() ) { self.id = id self.role = role self.text = text self.reasoning = reasoning self.attachments = attachments self.citations = citations self.modelID = modelID self.provider = provider self.usage = usage self.estimatedCost = estimatedCost self.createdAt = createdAt } } /// A file attached to a user message. Images go to vision models as base64; /// text files are injected into the prompt. struct Attachment: Codable, Identifiable, Hashable { enum Kind: String, Codable { case image case textFile } let id: UUID var kind: Kind var fileName: String /// image: raw image bytes; textFile: UTF-8 contents. var data: Data /// MIME type for images (image/png, image/jpeg, image/webp, image/gif). var mimeType: String init(id: UUID = UUID(), kind: Kind, fileName: String, data: Data, mimeType: String) { self.id = id self.kind = kind self.fileName = fileName self.data = data self.mimeType = mimeType } } /// A numbered web source backing an assistant answer (Perplexity sonar family). struct Citation: Codable, Identifiable, Hashable { let id: UUID var index: Int var url: URL var title: String? init(id: UUID = UUID(), index: Int, url: URL, title: String? = nil) { self.id = id self.index = index self.url = url self.title = title } }