207 lines
5.8 KiB
Swift
207 lines
5.8 KiB
Swift
import Foundation
|
|
|
|
public enum Provider: String, CaseIterable, Codable, Hashable, Sendable {
|
|
case claude = "Claude"
|
|
case codex = "Codex"
|
|
case gemini = "Gemini"
|
|
case hermes = "Hermes"
|
|
case opencode = "OpenCode"
|
|
}
|
|
|
|
public struct UsageRecord: Identifiable, Hashable, Sendable {
|
|
public let id: UUID
|
|
public let provider: Provider
|
|
public let source: String
|
|
public let sourcePath: String
|
|
public let timestamp: Date
|
|
public let sessionID: String
|
|
public let model: String
|
|
public let inputTokens: Int
|
|
public let outputTokens: Int
|
|
public let cachedTokens: Int
|
|
public let reasoningTokens: Int
|
|
public let toolTokens: Int
|
|
public let totalTokens: Int
|
|
|
|
public init(
|
|
id: UUID = UUID(),
|
|
provider: Provider,
|
|
source: String,
|
|
sourcePath: String,
|
|
timestamp: Date,
|
|
sessionID: String,
|
|
model: String,
|
|
inputTokens: Int,
|
|
outputTokens: Int,
|
|
cachedTokens: Int,
|
|
reasoningTokens: Int,
|
|
toolTokens: Int,
|
|
explicitTotal: Int? = nil
|
|
) {
|
|
self.id = id
|
|
self.provider = provider
|
|
self.source = source
|
|
self.sourcePath = sourcePath
|
|
self.timestamp = timestamp
|
|
self.sessionID = sessionID
|
|
self.model = model
|
|
self.inputTokens = inputTokens
|
|
self.outputTokens = outputTokens
|
|
self.cachedTokens = cachedTokens
|
|
self.reasoningTokens = reasoningTokens
|
|
self.toolTokens = toolTokens
|
|
self.totalTokens = explicitTotal ?? inputTokens + outputTokens + cachedTokens + reasoningTokens + toolTokens
|
|
}
|
|
}
|
|
|
|
public struct TokenBucket: Hashable, Sendable {
|
|
public let provider: Provider?
|
|
public var inputTokens: Int = 0
|
|
public var outputTokens: Int = 0
|
|
public var cachedTokens: Int = 0
|
|
public var reasoningTokens: Int = 0
|
|
public var toolTokens: Int = 0
|
|
public var totalTokens: Int = 0
|
|
public var count: Int = 0
|
|
|
|
public init(provider: Provider? = nil) {
|
|
self.provider = provider
|
|
}
|
|
|
|
public mutating func add(_ record: UsageRecord) {
|
|
add(
|
|
inputTokens: record.inputTokens,
|
|
outputTokens: record.outputTokens,
|
|
cachedTokens: record.cachedTokens,
|
|
reasoningTokens: record.reasoningTokens,
|
|
toolTokens: record.toolTokens,
|
|
totalTokens: record.totalTokens
|
|
)
|
|
}
|
|
|
|
public mutating func add(
|
|
inputTokens: Int,
|
|
outputTokens: Int,
|
|
cachedTokens: Int,
|
|
reasoningTokens: Int,
|
|
toolTokens: Int,
|
|
totalTokens: Int
|
|
) {
|
|
self.inputTokens += inputTokens
|
|
self.outputTokens += outputTokens
|
|
self.cachedTokens += cachedTokens
|
|
self.reasoningTokens += reasoningTokens
|
|
self.toolTokens += toolTokens
|
|
self.totalTokens += totalTokens
|
|
count += 1
|
|
}
|
|
}
|
|
|
|
public struct SummaryCards: Hashable, Sendable {
|
|
public let todayTokens: Int
|
|
public let monthTokens: Int
|
|
public let totalTokens: Int
|
|
public let cacheHitRate: Int
|
|
public let sessionCount: Int
|
|
}
|
|
|
|
public struct DailyPoint: Identifiable, Hashable, Sendable {
|
|
public let id = UUID()
|
|
public let date: Date
|
|
public let values: [Provider: Int]
|
|
}
|
|
|
|
public enum TrendMode: String, CaseIterable, Sendable {
|
|
case daily = "每日"
|
|
case weekly = "每周"
|
|
case monthly = "每月"
|
|
case yearly = "每年"
|
|
}
|
|
|
|
public struct ToolRow: Identifiable, Hashable, Sendable {
|
|
public let id = UUID()
|
|
public let provider: Provider
|
|
public let inputTokens: Int
|
|
public let outputTokens: Int
|
|
public let cachedTokens: Int
|
|
public let totalTokens: Int
|
|
public let trendTokens: [Int]
|
|
}
|
|
|
|
public struct DailyUsageDetail: Identifiable, Hashable, Sendable {
|
|
public var id: Date { date }
|
|
|
|
public let date: Date
|
|
public var inputTokens: Int
|
|
public var outputTokens: Int
|
|
public var cachedTokens: Int
|
|
public var totalTokens: Int
|
|
public var providerTotals: [Provider: Int]
|
|
|
|
public init(
|
|
date: Date,
|
|
inputTokens: Int = 0,
|
|
outputTokens: Int = 0,
|
|
cachedTokens: Int = 0,
|
|
totalTokens: Int = 0,
|
|
providerTotals: [Provider: Int] = [:]
|
|
) {
|
|
self.date = date
|
|
self.inputTokens = inputTokens
|
|
self.outputTokens = outputTokens
|
|
self.cachedTokens = cachedTokens
|
|
self.totalTokens = totalTokens
|
|
self.providerTotals = providerTotals
|
|
}
|
|
|
|
public mutating func add(_ record: UsageRecord) {
|
|
add(
|
|
provider: record.provider,
|
|
inputTokens: record.inputTokens,
|
|
outputTokens: record.outputTokens,
|
|
cachedTokens: record.cachedTokens,
|
|
totalTokens: record.totalTokens
|
|
)
|
|
}
|
|
|
|
public mutating func add(
|
|
provider: Provider,
|
|
inputTokens: Int,
|
|
outputTokens: Int,
|
|
cachedTokens: Int,
|
|
totalTokens: Int
|
|
) {
|
|
self.inputTokens += inputTokens
|
|
self.outputTokens += outputTokens
|
|
self.cachedTokens += cachedTokens
|
|
self.totalTokens += totalTokens
|
|
providerTotals[provider, default: 0] += totalTokens
|
|
}
|
|
}
|
|
|
|
public struct Insight: Identifiable, Hashable, Sendable {
|
|
public enum Tone: String, Sendable {
|
|
case warning
|
|
case positive
|
|
case accent
|
|
}
|
|
|
|
public let id = UUID()
|
|
public let tone: Tone
|
|
public let title: String
|
|
public let message: String
|
|
}
|
|
|
|
public struct UsageSummary: Sendable {
|
|
public let generatedAt: Date
|
|
public let records: [UsageRecord]
|
|
public let cards: SummaryCards
|
|
public let providerTotals: [Provider: TokenBucket]
|
|
public let todayProviderTotals: [Provider: TokenBucket]
|
|
public let dailyTrend: [DailyPoint]
|
|
public let trendPointsByMode: [TrendMode: [DailyPoint]]
|
|
public let dailyDetails: [DailyUsageDetail]
|
|
public let toolRows: [ToolRow]
|
|
public let insights: [Insight]
|
|
}
|