import Foundation struct GatewayCostUsageTotals: Codable { let input: Int let output: Int let cacheRead: Int let cacheWrite: Int let totalTokens: Int let totalCost: Double let missingCostEntries: Int } struct GatewayCostUsageDay: Codable { let date: String private let totals: GatewayCostUsageTotals var input: Int { self.totals.input } var output: Int { self.totals.output } var cacheRead: Int { self.totals.cacheRead } var cacheWrite: Int { self.totals.cacheWrite } var totalTokens: Int { self.totals.totalTokens } var totalCost: Double { self.totals.totalCost } var missingCostEntries: Int { self.totals.missingCostEntries } init( date: String, input: Int, output: Int, cacheRead: Int, cacheWrite: Int, totalTokens: Int, totalCost: Double, missingCostEntries: Int) { self.date = date self.totals = GatewayCostUsageTotals( input: input, output: output, cacheRead: cacheRead, cacheWrite: cacheWrite, totalTokens: totalTokens, totalCost: totalCost, missingCostEntries: missingCostEntries) } private enum CodingKeys: String, CodingKey { case date case input case output case cacheRead case cacheWrite case totalTokens case totalCost case missingCostEntries } init(from decoder: Decoder) throws { let c = try decoder.container(keyedBy: CodingKeys.self) self.date = try c.decode(String.self, forKey: .date) self.totals = try GatewayCostUsageTotals( input: c.decode(Int.self, forKey: .input), output: c.decode(Int.self, forKey: .output), cacheRead: c.decode(Int.self, forKey: .cacheRead), cacheWrite: c.decode(Int.self, forKey: .cacheWrite), totalTokens: c.decode(Int.self, forKey: .totalTokens), totalCost: c.decode(Double.self, forKey: .totalCost), missingCostEntries: c.decode(Int.self, forKey: .missingCostEntries)) } func encode(to encoder: Encoder) throws { var c = encoder.container(keyedBy: CodingKeys.self) try c.encode(self.date, forKey: .date) try c.encode(self.input, forKey: .input) try c.encode(self.output, forKey: .output) try c.encode(self.cacheRead, forKey: .cacheRead) try c.encode(self.cacheWrite, forKey: .cacheWrite) try c.encode(self.totalTokens, forKey: .totalTokens) try c.encode(self.totalCost, forKey: .totalCost) try c.encode(self.missingCostEntries, forKey: .missingCostEntries) } } struct GatewayCostUsageSummary: Codable { let updatedAt: Double let days: Int let daily: [GatewayCostUsageDay] let totals: GatewayCostUsageTotals } enum CostUsageFormatting { static func formatUsd(_ value: Double?) -> String? { guard let value, value.isFinite else { return nil } if value >= 1 { return String(format: "$%.2f", value) } if value >= 0.01 { return String(format: "$%.2f", value) } return String(format: "$%.4f", value) } static func formatTokenCount(_ value: Int?) -> String? { guard let value else { return nil } let safe = max(0, value) if safe >= 1_000_000 { return String(format: "%.1fm", Double(safe) / 1_000_000.0) } if safe >= 1000 { return safe >= 10000 ? String(format: "%.0fk", Double(safe) / 1000.0) : String(format: "%.1fk", Double(safe) / 1000.0) } return String(safe) } } @MainActor enum CostUsageLoader { static func loadSummary() async throws -> GatewayCostUsageSummary { let data = try await ControlChannel.shared.request( method: "usage.cost", params: nil, timeoutMs: 7000) return try JSONDecoder().decode(GatewayCostUsageSummary.self, from: data) } }