Files
steipete--codexbar/Sources/CodexBarCore/Providers/Claude/ClaudeAdminAPIUsageSnapshot.swift
T
2026-07-13 12:22:33 +08:00

243 lines
8.1 KiB
Swift

import Foundation
public struct ClaudeAdminAPIUsageSnapshot: Codable, Equatable, Sendable {
public struct DailyBucket: Codable, Equatable, Sendable, Identifiable {
public let day: String
public let startTime: Date
public let endTime: Date
public let costUSD: Double
public let inputTokens: Int
public let cacheCreationInputTokens: Int
public let cacheReadInputTokens: Int
public let outputTokens: Int
public let totalTokens: Int
public let costItems: [CostBreakdown]
public let models: [ModelBreakdown]
public var id: String {
self.day
}
public init(
day: String,
startTime: Date,
endTime: Date,
costUSD: Double,
inputTokens: Int,
cacheCreationInputTokens: Int,
cacheReadInputTokens: Int,
outputTokens: Int,
totalTokens: Int,
costItems: [CostBreakdown],
models: [ModelBreakdown])
{
self.day = day
self.startTime = startTime
self.endTime = endTime
self.costUSD = costUSD
self.inputTokens = inputTokens
self.cacheCreationInputTokens = cacheCreationInputTokens
self.cacheReadInputTokens = cacheReadInputTokens
self.outputTokens = outputTokens
self.totalTokens = totalTokens
self.costItems = costItems
self.models = models
}
}
public struct CostBreakdown: Codable, Equatable, Sendable, Identifiable {
public let name: String
public let costUSD: Double
public var id: String {
self.name
}
public init(name: String, costUSD: Double) {
self.name = name
self.costUSD = costUSD
}
}
public struct ModelBreakdown: Codable, Equatable, Sendable, Identifiable {
public let name: String
public let inputTokens: Int
public let cacheCreationInputTokens: Int
public let cacheReadInputTokens: Int
public let outputTokens: Int
public let totalTokens: Int
public var id: String {
self.name
}
public init(
name: String,
inputTokens: Int,
cacheCreationInputTokens: Int,
cacheReadInputTokens: Int,
outputTokens: Int,
totalTokens: Int)
{
self.name = name
self.inputTokens = inputTokens
self.cacheCreationInputTokens = cacheCreationInputTokens
self.cacheReadInputTokens = cacheReadInputTokens
self.outputTokens = outputTokens
self.totalTokens = totalTokens
}
}
public struct Summary: Equatable, Sendable {
public let costUSD: Double
public let inputTokens: Int
public let cacheCreationInputTokens: Int
public let cacheReadInputTokens: Int
public let outputTokens: Int
public let totalTokens: Int
public init(
costUSD: Double,
inputTokens: Int,
cacheCreationInputTokens: Int,
cacheReadInputTokens: Int,
outputTokens: Int,
totalTokens: Int)
{
self.costUSD = costUSD
self.inputTokens = inputTokens
self.cacheCreationInputTokens = cacheCreationInputTokens
self.cacheReadInputTokens = cacheReadInputTokens
self.outputTokens = outputTokens
self.totalTokens = totalTokens
}
}
public let daily: [DailyBucket]
public let updatedAt: Date
public init(daily: [DailyBucket], updatedAt: Date) {
self.daily = daily.sorted { $0.startTime < $1.startTime }
self.updatedAt = updatedAt
}
public var last30Days: Summary {
self.summary(days: 30)
}
public var last7Days: Summary {
self.summary(days: 7)
}
public var currentDay: Summary {
self.summary(forLocalDayContaining: self.updatedAt)
}
public var latestDay: Summary {
self.summary(days: 1)
}
public func summary(forLocalDayContaining date: Date, calendar _: Calendar = .current) -> Summary {
let selected = self.daily.filter { bucket in
CostUsageBucketInterval.contains(
date,
startTime: bucket.startTime,
endTime: bucket.endTime)
}
return Summary(
costUSD: selected.reduce(0) { $0 + $1.costUSD },
inputTokens: selected.reduce(0) { $0 + $1.inputTokens },
cacheCreationInputTokens: selected.reduce(0) { $0 + $1.cacheCreationInputTokens },
cacheReadInputTokens: selected.reduce(0) { $0 + $1.cacheReadInputTokens },
outputTokens: selected.reduce(0) { $0 + $1.outputTokens },
totalTokens: selected.reduce(0) { $0 + $1.totalTokens })
}
public func summary(days: Int) -> Summary {
let selected = self.daily.suffix(max(1, days))
return Summary(
costUSD: selected.reduce(0) { $0 + $1.costUSD },
inputTokens: selected.reduce(0) { $0 + $1.inputTokens },
cacheCreationInputTokens: selected.reduce(0) { $0 + $1.cacheCreationInputTokens },
cacheReadInputTokens: selected.reduce(0) { $0 + $1.cacheReadInputTokens },
outputTokens: selected.reduce(0) { $0 + $1.outputTokens },
totalTokens: selected.reduce(0) { $0 + $1.totalTokens })
}
public var topModels: [ModelBreakdown] {
var totals: [String: ModelAccumulator] = [:]
for day in self.daily {
for model in day.models {
totals[model.name, default: ModelAccumulator()].add(model)
}
}
return totals
.map { name, total in total.makeModel(name: name) }
.sorted {
if $0.totalTokens == $1.totalTokens { return $0.name < $1.name }
return $0.totalTokens > $1.totalTokens
}
}
public var topCostItems: [CostBreakdown] {
var totals: [String: Double] = [:]
for day in self.daily {
for item in day.costItems {
totals[item.name, default: 0] += item.costUSD
}
}
return totals
.map { CostBreakdown(name: $0.key, costUSD: $0.value) }
.sorted {
if $0.costUSD == $1.costUSD { return $0.name < $1.name }
return $0.costUSD > $1.costUSD
}
}
public func toUsageSnapshot() -> UsageSnapshot {
let total = self.last30Days
return UsageSnapshot(
primary: nil,
secondary: nil,
providerCost: ProviderCostSnapshot(
used: total.costUSD,
limit: 0,
currencyCode: "USD",
period: "Last 30 days",
updatedAt: self.updatedAt),
claudeAdminAPIUsage: self,
updatedAt: self.updatedAt,
identity: ProviderIdentitySnapshot(
providerID: .claude,
accountEmail: nil,
accountOrganization: nil,
loginMethod: "Admin API"))
}
private struct ModelAccumulator {
var inputTokens = 0
var cacheCreationInputTokens = 0
var cacheReadInputTokens = 0
var outputTokens = 0
var totalTokens = 0
mutating func add(_ model: ModelBreakdown) {
self.inputTokens += model.inputTokens
self.cacheCreationInputTokens += model.cacheCreationInputTokens
self.cacheReadInputTokens += model.cacheReadInputTokens
self.outputTokens += model.outputTokens
self.totalTokens += model.totalTokens
}
func makeModel(name: String) -> ModelBreakdown {
ModelBreakdown(
name: name,
inputTokens: self.inputTokens,
cacheCreationInputTokens: self.cacheCreationInputTokens,
cacheReadInputTokens: self.cacheReadInputTokens,
outputTokens: self.outputTokens,
totalTokens: self.totalTokens)
}
}
}