// // UsageStats.swift // Confab // // All-time usage statistics (aggregated from the messages table) // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen // // This file is part of Confab. // // Confab is licensed under the PolyForm Noncommercial License 1.0.0. // You may use, study, modify, and share it for any noncommercial // purpose. Commercial use — including selling Confab or any part of // it, standalone or bundled into another product or service — // requires a separate commercial license from the copyright holder. // // See the LICENSE file or // for // the full license text. For commercial licensing, contact Rune // Olsen via . import Foundation struct UsageStats: Sendable { var totalMessages: Int var totalTokens: Int var totalCost: Double var hasCostData: Bool var firstMessageDate: Date? var lastMessageDate: Date? nonisolated init( totalMessages: Int = 0, totalTokens: Int = 0, totalCost: Double = 0.0, hasCostData: Bool = false, firstMessageDate: Date? = nil, lastMessageDate: Date? = nil ) { self.totalMessages = totalMessages self.totalTokens = totalTokens self.totalCost = totalCost self.hasCostData = hasCostData self.firstMessageDate = firstMessageDate self.lastMessageDate = lastMessageDate } var totalTokensDisplay: String { if totalTokens >= 1_000_000 { return String(format: "%.1fM", Double(totalTokens) / 1_000_000) } else if totalTokens >= 1000 { return String(format: "%.1fK", Double(totalTokens) / 1000) } else { return "\(totalTokens)" } } var totalCostDisplay: String { hasCostData ? String(format: "$%.4f", totalCost) : "N/A" } } struct ModelUsageStat: Identifiable, Sendable { var id: String { modelId } let modelId: String var messageCount: Int var totalTokens: Int var totalCost: Double var hasCostData: Bool var lastUsed: Date nonisolated init( modelId: String, messageCount: Int, totalTokens: Int, totalCost: Double, hasCostData: Bool, lastUsed: Date ) { self.modelId = modelId self.messageCount = messageCount self.totalTokens = totalTokens self.totalCost = totalCost self.hasCostData = hasCostData self.lastUsed = lastUsed } var totalTokensDisplay: String { if totalTokens >= 1_000_000 { return String(format: "%.1fM", Double(totalTokens) / 1_000_000) } else if totalTokens >= 1000 { return String(format: "%.1fK", Double(totalTokens) / 1000) } else { return "\(totalTokens)" } } var totalCostDisplay: String { hasCostData ? String(format: "$%.4f", totalCost) : "N/A" } } struct ConversationUsageStat: Identifiable, Sendable { let conversationId: UUID var id: UUID { conversationId } var name: String var messageCount: Int var totalTokens: Int var totalCost: Double var hasCostData: Bool nonisolated init( conversationId: UUID, name: String, messageCount: Int, totalTokens: Int, totalCost: Double, hasCostData: Bool ) { self.conversationId = conversationId self.name = name self.messageCount = messageCount self.totalTokens = totalTokens self.totalCost = totalCost self.hasCostData = hasCostData } var totalTokensDisplay: String { if totalTokens >= 1_000_000 { return String(format: "%.1fM", Double(totalTokens) / 1_000_000) } else if totalTokens >= 1000 { return String(format: "%.1fK", Double(totalTokens) / 1000) } else { return "\(totalTokens)" } } var totalCostDisplay: String { hasCostData ? String(format: "$%.4f", totalCost) : "N/A" } }