import Foundation#if canImport(FoundationNetworking)import FoundationNetworking#endif struct ModelsDevPricingInfo: Codable, Equatable { var providerID: String var providerName: String? var modelID: String var modelName: String? var inputCostPerToken: Double var outputCostPerToken: Double var cacheReadInputCostPerToken: Double? var cacheCreationInputCostPerToken: Double? var contextWindow: Int? var thresholdTokens: Int? var inputCostPerTokenAboveThreshold: Double? var outputCostPerTokenAboveThreshold: Double? var cacheReadInputCostPerTokenAboveThreshold: Double? var cacheCreationInputCostPerTokenAboveThreshold: Double?} struct ModelsDevPricingLookup: Equatable { var pricing: ModelsDevPricingInfo var normalizedModelID: String} struct ModelsDevCatalog: Codable, Equatable { var providers: [String: ModelsDevProvider] init(providers: [String: ModelsDevProvider]) { self.providers = providers } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: ModelsDevAnyCodingKey.self) if let providersKey = ModelsDevAnyCodingKey(stringValue: "providers"), let decoded = try? container.decode([String: ModelsDevProvider].self, forKey: providersKey) { self.providers = decoded.reduce(into: [:]) { result, item in var provider = item.value provider.mapKey = provider.mapKey ?? item.key let providerID = ModelsDevProvider.normalizeProviderID(provider.id ?? item.key) result[providerID] = provider } return } var providers: [String: ModelsDevProvider] = [:] for key in container.allKeys { guard var provider = try? container.decode(ModelsDevProvider.self, forKey: key) else { continue } provider.mapKey = key.stringValue let providerID = ModelsDevProvider.normalizeProviderID(provider.id ?? key.stringValue) providers[providerID] = provider } self.providers = providers } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: ModelsDevAnyCodingKey.self) try container.encode(self.providers, forKey: ModelsDevAnyCodingKey(stringValue: "providers")!) } func pricing(providerID rawProviderID: String, modelID rawModelID: String) -> ModelsDevPricingLookup? { let providerID = ModelsDevProvider.normalizeProviderID(rawProviderID) return self.providers[providerID]?.pricing(modelID: rawModelID) } func isPlausibleRefresh() -> Bool { // These are the direct pricing sources CodexBar relies on. Requiring both // rejects empty/partial responses without comparing against a fallback- // enriched cache that intentionally grows as models.dev churns. ["anthropic", "openai"].allSatisfy { providerID in self.providers[providerID]?.models.values.contains(where: \.isPriceable) == true } } func mergingFallbackPricing(from cachedCatalog: ModelsDevCatalog) -> ModelsDevCatalog { var merged = self for (providerID, cachedProvider) in cachedCatalog.providers { let normalizedProviderID = ModelsDevProvider.normalizeProviderID(providerID) guard var provider = merged.providers[normalizedProviderID] else { merged.providers[normalizedProviderID] = cachedProvider continue } for (modelKey, cachedModel) in cachedProvider.models where cachedModel.isPriceable && !provider.containsPricedModel( withStableIdentity: cachedModel.stableIdentity) { let fallbackKey = provider.models[modelKey] == nil ? modelKey : "codexbar-fallback:\(modelKey):\(cachedModel.normalizedID)" provider.models[fallbackKey] = cachedModel } merged.providers[normalizedProviderID] = provider } return merged }} private struct ModelsDevAnyCodingKey: CodingKey { var intValue: Int? var stringValue: String init?(intValue: Int) { self.intValue = intValue self.stringValue = String(intValue) } init?(stringValue: String) { self.intValue = nil self.stringValue = stringValue }} struct ModelsDevProvider: Codable, Equatable { var id: String? var name: String? var models: [String: ModelsDevModel] var mapKey: String? private enum CodingKeys: String, CodingKey { case id case name case models } init(id: String?, name: String?, models: [String: ModelsDevModel], mapKey: String? = nil) { self.id = id self.name = name self.models = models self.mapKey = mapKey } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.id = try container.decodeIfPresent(String.self, forKey: .id) self.name = try container.decodeIfPresent(String.self, forKey: .name) let modelContainer = try container.nestedContainer(keyedBy: ModelsDevAnyCodingKey.self, forKey: .models) var models: [String: ModelsDevModel] = [:] for key in modelContainer.allKeys { guard let model = try? modelContainer.decode(ModelsDevModel.self, forKey: key) else { continue } models[key.stringValue] = model } self.models = models } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encodeIfPresent(self.id, forKey: .id) try container.encodeIfPresent(self.name, forKey: .name) try container.encode(self.models, forKey: .models) } static func normalizeProviderID(_ raw: String) -> String { raw.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() } func pricing(modelID rawModelID: String) -> ModelsDevPricingLookup? { let candidates = ModelsDevModelIDNormalizer.candidates(rawModelID) for candidate in candidates { if let model = self.models[candidate], let pricing = model.pricing(providerID: self.id ?? self.mapKey ?? "", providerName: self.name) { return ModelsDevPricingLookup(pricing: pricing, normalizedModelID: candidate) } for match in self.models.values where match.normalizedID == candidate { if let pricing = match.pricing(providerID: self.id ?? self.mapKey ?? "", providerName: self.name) { return ModelsDevPricingLookup(pricing: pricing, normalizedModelID: match.normalizedID) } } } return nil } func containsPricedModel(withStableIdentity modelID: String) -> Bool { self.models.values.contains { model in model.isPriceable && model.stableIdentity == modelID } }} struct ModelsDevModel: Codable, Equatable { var id: String var name: String? var cost: ModelsDevCost? var limit: ModelsDevLimit? var normalizedID: String { ModelsDevModelIDNormalizer.normalize(self.id) } var stableIdentity: String { ModelsDevModelIDNormalizer.stableIdentity(self.id) } var isPriceable: Bool { self.cost?.input != nil && self.cost?.output != nil } func pricing(providerID: String, providerName: String?) -> ModelsDevPricingInfo? { guard let input = self.cost?.input, let output = self.cost?.output else { return nil } // models.dev publishes USD per 1M tokens. CodexBar cost math uses USD per token. let unit = 1_000_000.0 let contextOver200K = self.cost?.contextOver200K return ModelsDevPricingInfo( providerID: ModelsDevProvider.normalizeProviderID(providerID), providerName: providerName, modelID: self.id, modelName: self.name, inputCostPerToken: input / unit, outputCostPerToken: output / unit, cacheReadInputCostPerToken: self.cost?.cacheRead.map { $0 / unit }, cacheCreationInputCostPerToken: self.cost?.cacheWrite.map { $0 / unit }, contextWindow: self.limit?.context, thresholdTokens: contextOver200K == nil ? nil : 200_000, inputCostPerTokenAboveThreshold: contextOver200K?.input.map { $0 / unit }, outputCostPerTokenAboveThreshold: contextOver200K?.output.map { $0 / unit }, cacheReadInputCostPerTokenAboveThreshold: contextOver200K?.cacheRead.map { $0 / unit }, cacheCreationInputCostPerTokenAboveThreshold: contextOver200K?.cacheWrite.map { $0 / unit }) }} struct ModelsDevCost: Codable, Equatable { var input: Double? var output: Double? var cacheRead: Double? var cacheWrite: Double? var contextOver200K: ModelsDevContextOver200KCost? private enum CodingKeys: String, CodingKey { case input case output case cacheRead = "cache_read" case cacheWrite = "cache_write" case contextOver200K = "context_over_200k" }} struct ModelsDevContextOver200KCost: Codable, Equatable { var input: Double? var output: Double? var cacheRead: Double? var cacheWrite: Double? private enum CodingKeys: String, CodingKey { case input case output case cacheRead = "cache_read" case cacheWrite = "cache_write" }} struct ModelsDevLimit: Codable, Equatable { var context: Int?} enum ModelsDevModelIDNormalizer { static func normalize(_ raw: String) -> String { raw.trimmingCharacters(in: .whitespacesAndNewlines) } static func stableIdentity(_ raw: String) -> String { let normalized = self.normalize(raw) if let atSign = normalized.firstIndex(of: "@") { let base = String(normalized[..<atSign]) let suffix = String(normalized[normalized.index(after: atSign)...]) if suffix.range(of: #"^\d{8}$"#, options: .regularExpression) != nil { return "\(self.canonicalAliasIdentity(base))-\(suffix)" } } return self.canonicalAliasIdentity(normalized) } private static func canonicalAliasIdentity(_ raw: String) -> String { self.candidates(raw, preserveDatedSnapshots: true).reversed().lazy .map { candidate in guard candidate.hasSuffix("@default") else { return candidate } return String(candidate.dropLast("@default".count)) } .first { !$0.isEmpty } ?? self.normalize(raw) } static func candidates(_ raw: String, preserveDatedSnapshots: Bool = false) -> [String] { var candidates: [String] = [] func append(_ value: String) { let normalized = self.normalize(value) guard !normalized.isEmpty, !candidates.contains(normalized) else { return } candidates.append(normalized) } let trimmed = raw.trimmingCharacters(in: .whitespacesAndNewlines) append(trimmed) if trimmed.hasPrefix("openai/") { append(String(trimmed.dropFirst("openai/".count))) } if trimmed.hasPrefix("anthropic.") { append(String(trimmed.dropFirst("anthropic.".count))) } if let lastDot = trimmed.lastIndex(of: "."), trimmed.contains("claude-") { let tail = String(trimmed[trimmed.index(after: lastDot)...]) if tail.hasPrefix("claude-") { append(tail) } } var index = 0 while index < candidates.count { let candidate = candidates[index] if let atSign = candidate.firstIndex(of: "@") { let base = String(candidate[..<atSign]) let suffix = String(candidate[candidate.index(after: atSign)...]) if suffix.range(of: #"^\d{8}$"#, options: .regularExpression) != nil { append("\(base)-\(suffix)") } append(base) } else if candidate.hasPrefix("claude-") { append("\(candidate)@default") } if !preserveDatedSnapshots { if let dated = candidate.range(of: #"-\d{4}-\d{2}-\d{2}$"#, options: .regularExpression) { append(String(candidate[..<dated.lowerBound])) } if let compactDate = candidate.range(of: #"-\d{8}$"#, options: .regularExpression) { append(String(candidate[..<compactDate.lowerBound])) } } if let version = candidate.range(of: #"-v\d+:\d+$"#, options: .regularExpression) { var base = candidate base.removeSubrange(version) append(base) } index += 1 } return candidates }} struct ModelsDevCacheArtifact: Codable, Equatable { var version: Int var fetchedAt: Date var catalog: ModelsDevCatalog} struct ModelsDevCacheLoadResult: Equatable { var artifact: ModelsDevCacheArtifact? var isStale: Bool var error: ModelsDevCache.Error?} /// In-memory memo for the decoded models.dev catalog, keyed by file path + on-disk identity.////// `ModelsDevCache.load` is called once per usage row whenever a cost lookup is performed without a/// pre-resolved catalog (see `CostUsagePricing.modelsDevLookup`). Without this memo, scanning a large/// `~/.codex` history re-reads and re-decodes the ~800 KB catalog JSON for every row, which pegs the CPU/// and freezes the menu during a refresh.////// The full load *outcome* is memoized, not just successful decodes: a corrupt or wrong-version cache is/// read and decode-attempted exactly as expensively as a valid one, so caching only successes would leave/// the per-row storm in place whenever the cache is unreadable. Reusing the outcome while the file is/// unchanged keeps every fallback path cheap.private final class ModelsDevCacheMemo: @unchecked Sendable { enum Outcome { case decoded(ModelsDevCacheArtifact) case failure(ModelsDevCache.Error) } private struct Entry { let modificationDate: Date? let size: Int? let outcome: Outcome } private let lock = NSLock() private var entries: [String: Entry] = [:] func outcome(path: String, modificationDate: Date?, size: Int?) -> Outcome? { self.lock.lock() defer { self.lock.unlock() } guard let entry = self.entries[path], entry.modificationDate == modificationDate, entry.size == size else { return nil } return entry.outcome } func store(path: String, modificationDate: Date?, size: Int?, outcome: Outcome) { self.lock.lock() defer { self.lock.unlock() } self.entries[path] = Entry(modificationDate: modificationDate, size: size, outcome: outcome) } func invalidate(path: String) { self.lock.lock() defer { self.lock.unlock() } self.entries.removeValue(forKey: path) }} enum ModelsDevCache { enum Error: Swift.Error, Equatable { case unreadable case invalidVersion case invalidJSON } static let artifactVersion = 1 static let ttlSeconds: TimeInterval = 24 * 60 * 60 private static let memo = ModelsDevCacheMemo() private static func fileMetadata(at url: URL) -> (modificationDate: Date?, size: Int?) { guard let attributes = try? FileManager.default.attributesOfItem(atPath: url.path) else { return (nil, nil) } let modificationDate = attributes[.modificationDate] as? Date let size = (attributes[.size] as? NSNumber)?.intValue return (modificationDate, size) } private static func defaultCacheRoot() -> URL { let root = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first! return root.appendingPathComponent("CodexBar", isDirectory: true) } static func cacheFileURL(cacheRoot: URL? = nil) -> URL { let root = cacheRoot ?? self.defaultCacheRoot() return root .appendingPathComponent("model-pricing", isDirectory: true) .appendingPathComponent("models-dev-v\(Self.artifactVersion).json", isDirectory: false) } static func load(now: Date = Date(), cacheRoot: URL? = nil) -> ModelsDevCacheLoadResult { let url = self.cacheFileURL(cacheRoot: cacheRoot) let metadata = Self.fileMetadata(at: url) // Staleness depends on `now`, so the result is always rebuilt; only the read+decode outcome is memoized. if let outcome = Self.memo.outcome( path: url.path, modificationDate: metadata.modificationDate, size: metadata.size) { return Self.result(for: outcome, now: now) } let outcome = Self.readOutcome(at: url) Self.memo.store( path: url.path, modificationDate: metadata.modificationDate, size: metadata.size, outcome: outcome) return Self.result(for: outcome, now: now) } private static func readOutcome(at url: URL) -> ModelsDevCacheMemo.Outcome { guard let data = try? Data(contentsOf: url) else { return .failure(.unreadable) } let decoder = JSONDecoder() decoder.dateDecodingStrategy = .iso8601 guard let decoded = try? decoder.decode(ModelsDevCacheArtifact.self, from: data) else { return .failure(.invalidJSON) } guard decoded.version == Self.artifactVersion else { return .failure(.invalidVersion) } return .decoded(decoded) } private static func result(for outcome: ModelsDevCacheMemo.Outcome, now: Date) -> ModelsDevCacheLoadResult { switch outcome { case let .decoded(artifact): ModelsDevCacheLoadResult( artifact: artifact, isStale: now.timeIntervalSince(artifact.fetchedAt) > Self.ttlSeconds, error: nil) case let .failure(error): ModelsDevCacheLoadResult(artifact: nil, isStale: true, error: error) } }Lines 1–500 of 611. Load more
import Foundation #if canImport(FoundationNetworking) import FoundationNetworking #endif struct ModelsDevPricingInfo: Codable, Equatable { var providerID: String var providerName: String? var modelID: String var modelName: String? var inputCostPerToken: Double var outputCostPerToken: Double var cacheReadInputCostPerToken: Double? var cacheCreationInputCostPerToken: Double? var contextWindow: Int? var thresholdTokens: Int? var inputCostPerTokenAboveThreshold: Double? var outputCostPerTokenAboveThreshold: Double? var cacheReadInputCostPerTokenAboveThreshold: Double? var cacheCreationInputCostPerTokenAboveThreshold: Double? } struct ModelsDevPricingLookup: Equatable { var pricing: ModelsDevPricingInfo var normalizedModelID: String } struct ModelsDevCatalog: Codable, Equatable { var providers: [String: ModelsDevProvider] init(providers: [String: ModelsDevProvider]) { self.providers = providers } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: ModelsDevAnyCodingKey.self) if let providersKey = ModelsDevAnyCodingKey(stringValue: "providers"), let decoded = try? container.decode([String: ModelsDevProvider].self, forKey: providersKey) { self.providers = decoded.reduce(into: [:]) { result, item in var provider = item.value provider.mapKey = provider.mapKey ?? item.key let providerID = ModelsDevProvider.normalizeProviderID(provider.id ?? item.key) result[providerID] = provider } return } var providers: [String: ModelsDevProvider] = [:] for key in container.allKeys { guard var provider = try? container.decode(ModelsDevProvider.self, forKey: key) else { continue } provider.mapKey = key.stringValue let providerID = ModelsDevProvider.normalizeProviderID(provider.id ?? key.stringValue) providers[providerID] = provider } self.providers = providers } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: ModelsDevAnyCodingKey.self) try container.encode(self.providers, forKey: ModelsDevAnyCodingKey(stringValue: "providers")!) } func pricing(providerID rawProviderID: String, modelID rawModelID: String) -> ModelsDevPricingLookup? { let providerID = ModelsDevProvider.normalizeProviderID(rawProviderID) return self.providers[providerID]?.pricing(modelID: rawModelID) } func isPlausibleRefresh() -> Bool { // These are the direct pricing sources CodexBar relies on. Requiring both // rejects empty/partial responses without comparing against a fallback- // enriched cache that intentionally grows as models.dev churns. ["anthropic", "openai"].allSatisfy { providerID in self.providers[providerID]?.models.values.contains(where: \.isPriceable) == true } } func mergingFallbackPricing(from cachedCatalog: ModelsDevCatalog) -> ModelsDevCatalog { var merged = self for (providerID, cachedProvider) in cachedCatalog.providers { let normalizedProviderID = ModelsDevProvider.normalizeProviderID(providerID) guard var provider = merged.providers[normalizedProviderID] else { merged.providers[normalizedProviderID] = cachedProvider continue } for (modelKey, cachedModel) in cachedProvider.models where cachedModel.isPriceable && !provider.containsPricedModel( withStableIdentity: cachedModel.stableIdentity) { let fallbackKey = provider.models[modelKey] == nil ? modelKey : "codexbar-fallback:\(modelKey):\(cachedModel.normalizedID)" provider.models[fallbackKey] = cachedModel } merged.providers[normalizedProviderID] = provider } return merged } } private struct ModelsDevAnyCodingKey: CodingKey { var intValue: Int? var stringValue: String init?(intValue: Int) { self.intValue = intValue self.stringValue = String(intValue) } init?(stringValue: String) { self.intValue = nil self.stringValue = stringValue } } struct ModelsDevProvider: Codable, Equatable { var id: String? var name: String? var models: [String: ModelsDevModel] var mapKey: String? private enum CodingKeys: String, CodingKey { case id case name case models } init(id: String?, name: String?, models: [String: ModelsDevModel], mapKey: String? = nil) { self.id = id self.name = name self.models = models self.mapKey = mapKey } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.id = try container.decodeIfPresent(String.self, forKey: .id) self.name = try container.decodeIfPresent(String.self, forKey: .name) let modelContainer = try container.nestedContainer(keyedBy: ModelsDevAnyCodingKey.self, forKey: .models) var models: [String: ModelsDevModel] = [:] for key in modelContainer.allKeys { guard let model = try? modelContainer.decode(ModelsDevModel.self, forKey: key) else { continue } models[key.stringValue] = model } self.models = models } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encodeIfPresent(self.id, forKey: .id) try container.encodeIfPresent(self.name, forKey: .name) try container.encode(self.models, forKey: .models) } static func normalizeProviderID(_ raw: String) -> String { raw.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() } func pricing(modelID rawModelID: String) -> ModelsDevPricingLookup? { let candidates = ModelsDevModelIDNormalizer.candidates(rawModelID) for candidate in candidates { if let model = self.models[candidate], let pricing = model.pricing(providerID: self.id ?? self.mapKey ?? "", providerName: self.name) { return ModelsDevPricingLookup(pricing: pricing, normalizedModelID: candidate) } for match in self.models.values where match.normalizedID == candidate { if let pricing = match.pricing(providerID: self.id ?? self.mapKey ?? "", providerName: self.name) { return ModelsDevPricingLookup(pricing: pricing, normalizedModelID: match.normalizedID) } } } return nil } func containsPricedModel(withStableIdentity modelID: String) -> Bool { self.models.values.contains { model in model.isPriceable && model.stableIdentity == modelID } } } struct ModelsDevModel: Codable, Equatable { var id: String var name: String? var cost: ModelsDevCost? var limit: ModelsDevLimit? var normalizedID: String { ModelsDevModelIDNormalizer.normalize(self.id) } var stableIdentity: String { ModelsDevModelIDNormalizer.stableIdentity(self.id) } var isPriceable: Bool { self.cost?.input != nil && self.cost?.output != nil } func pricing(providerID: String, providerName: String?) -> ModelsDevPricingInfo? { guard let input = self.cost?.input, let output = self.cost?.output else { return nil } // models.dev publishes USD per 1M tokens. CodexBar cost math uses USD per token. let unit = 1_000_000.0 let contextOver200K = self.cost?.contextOver200K return ModelsDevPricingInfo( providerID: ModelsDevProvider.normalizeProviderID(providerID), providerName: providerName, modelID: self.id, modelName: self.name, inputCostPerToken: input / unit, outputCostPerToken: output / unit, cacheReadInputCostPerToken: self.cost?.cacheRead.map { $0 / unit }, cacheCreationInputCostPerToken: self.cost?.cacheWrite.map { $0 / unit }, contextWindow: self.limit?.context, thresholdTokens: contextOver200K == nil ? nil : 200_000, inputCostPerTokenAboveThreshold: contextOver200K?.input.map { $0 / unit }, outputCostPerTokenAboveThreshold: contextOver200K?.output.map { $0 / unit }, cacheReadInputCostPerTokenAboveThreshold: contextOver200K?.cacheRead.map { $0 / unit }, cacheCreationInputCostPerTokenAboveThreshold: contextOver200K?.cacheWrite.map { $0 / unit }) } } struct ModelsDevCost: Codable, Equatable { var input: Double? var output: Double? var cacheRead: Double? var cacheWrite: Double? var contextOver200K: ModelsDevContextOver200KCost? private enum CodingKeys: String, CodingKey { case input case output case cacheRead = "cache_read" case cacheWrite = "cache_write" case contextOver200K = "context_over_200k" } } struct ModelsDevContextOver200KCost: Codable, Equatable { var input: Double? var output: Double? var cacheRead: Double? var cacheWrite: Double? private enum CodingKeys: String, CodingKey { case input case output case cacheRead = "cache_read" case cacheWrite = "cache_write" } } struct ModelsDevLimit: Codable, Equatable { var context: Int? } enum ModelsDevModelIDNormalizer { static func normalize(_ raw: String) -> String { raw.trimmingCharacters(in: .whitespacesAndNewlines) } static func stableIdentity(_ raw: String) -> String { let normalized = self.normalize(raw) if let atSign = normalized.firstIndex(of: "@") { let base = String(normalized[..<atSign]) let suffix = String(normalized[normalized.index(after: atSign)...]) if suffix.range(of: #"^\d{8}$"#, options: .regularExpression) != nil { return "\(self.canonicalAliasIdentity(base))-\(suffix)" } } return self.canonicalAliasIdentity(normalized) } private static func canonicalAliasIdentity(_ raw: String) -> String { self.candidates(raw, preserveDatedSnapshots: true).reversed().lazy .map { candidate in guard candidate.hasSuffix("@default") else { return candidate } return String(candidate.dropLast("@default".count)) } .first { !$0.isEmpty } ?? self.normalize(raw) } static func candidates(_ raw: String, preserveDatedSnapshots: Bool = false) -> [String] { var candidates: [String] = [] func append(_ value: String) { let normalized = self.normalize(value) guard !normalized.isEmpty, !candidates.contains(normalized) else { return } candidates.append(normalized) } let trimmed = raw.trimmingCharacters(in: .whitespacesAndNewlines) append(trimmed) if trimmed.hasPrefix("openai/") { append(String(trimmed.dropFirst("openai/".count))) } if trimmed.hasPrefix("anthropic.") { append(String(trimmed.dropFirst("anthropic.".count))) } if let lastDot = trimmed.lastIndex(of: "."), trimmed.contains("claude-") { let tail = String(trimmed[trimmed.index(after: lastDot)...]) if tail.hasPrefix("claude-") { append(tail) } } var index = 0 while index < candidates.count { let candidate = candidates[index] if let atSign = candidate.firstIndex(of: "@") { let base = String(candidate[..<atSign]) let suffix = String(candidate[candidate.index(after: atSign)...]) if suffix.range(of: #"^\d{8}$"#, options: .regularExpression) != nil { append("\(base)-\(suffix)") } append(base) } else if candidate.hasPrefix("claude-") { append("\(candidate)@default") } if !preserveDatedSnapshots { if let dated = candidate.range(of: #"-\d{4}-\d{2}-\d{2}$"#, options: .regularExpression) { append(String(candidate[..<dated.lowerBound])) } if let compactDate = candidate.range(of: #"-\d{8}$"#, options: .regularExpression) { append(String(candidate[..<compactDate.lowerBound])) } } if let version = candidate.range(of: #"-v\d+:\d+$"#, options: .regularExpression) { var base = candidate base.removeSubrange(version) append(base) } index += 1 } return candidates } } struct ModelsDevCacheArtifact: Codable, Equatable { var version: Int var fetchedAt: Date var catalog: ModelsDevCatalog } struct ModelsDevCacheLoadResult: Equatable { var artifact: ModelsDevCacheArtifact? var isStale: Bool var error: ModelsDevCache.Error? } /// In-memory memo for the decoded models.dev catalog, keyed by file path + on-disk identity. /// /// `ModelsDevCache.load` is called once per usage row whenever a cost lookup is performed without a /// pre-resolved catalog (see `CostUsagePricing.modelsDevLookup`). Without this memo, scanning a large /// `~/.codex` history re-reads and re-decodes the ~800 KB catalog JSON for every row, which pegs the CPU /// and freezes the menu during a refresh. /// /// The full load *outcome* is memoized, not just successful decodes: a corrupt or wrong-version cache is /// read and decode-attempted exactly as expensively as a valid one, so caching only successes would leave /// the per-row storm in place whenever the cache is unreadable. Reusing the outcome while the file is /// unchanged keeps every fallback path cheap. private final class ModelsDevCacheMemo: @unchecked Sendable { enum Outcome { case decoded(ModelsDevCacheArtifact) case failure(ModelsDevCache.Error) } private struct Entry { let modificationDate: Date? let size: Int? let outcome: Outcome } private let lock = NSLock() private var entries: [String: Entry] = [:] func outcome(path: String, modificationDate: Date?, size: Int?) -> Outcome? { self.lock.lock() defer { self.lock.unlock() } guard let entry = self.entries[path], entry.modificationDate == modificationDate, entry.size == size else { return nil } return entry.outcome } func store(path: String, modificationDate: Date?, size: Int?, outcome: Outcome) { self.lock.lock() defer { self.lock.unlock() } self.entries[path] = Entry(modificationDate: modificationDate, size: size, outcome: outcome) } func invalidate(path: String) { self.lock.lock() defer { self.lock.unlock() } self.entries.removeValue(forKey: path) } } enum ModelsDevCache { enum Error: Swift.Error, Equatable { case unreadable case invalidVersion case invalidJSON } static let artifactVersion = 1 static let ttlSeconds: TimeInterval = 24 * 60 * 60 private static let memo = ModelsDevCacheMemo() private static func fileMetadata(at url: URL) -> (modificationDate: Date?, size: Int?) { guard let attributes = try? FileManager.default.attributesOfItem(atPath: url.path) else { return (nil, nil) } let modificationDate = attributes[.modificationDate] as? Date let size = (attributes[.size] as? NSNumber)?.intValue return (modificationDate, size) } private static func defaultCacheRoot() -> URL { let root = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first! return root.appendingPathComponent("CodexBar", isDirectory: true) } static func cacheFileURL(cacheRoot: URL? = nil) -> URL { let root = cacheRoot ?? self.defaultCacheRoot() return root .appendingPathComponent("model-pricing", isDirectory: true) .appendingPathComponent("models-dev-v\(Self.artifactVersion).json", isDirectory: false) } static func load(now: Date = Date(), cacheRoot: URL? = nil) -> ModelsDevCacheLoadResult { let url = self.cacheFileURL(cacheRoot: cacheRoot) let metadata = Self.fileMetadata(at: url) // Staleness depends on `now`, so the result is always rebuilt; only the read+decode outcome is memoized. if let outcome = Self.memo.outcome( path: url.path, modificationDate: metadata.modificationDate, size: metadata.size) { return Self.result(for: outcome, now: now) } let outcome = Self.readOutcome(at: url) Self.memo.store( path: url.path, modificationDate: metadata.modificationDate, size: metadata.size, outcome: outcome) return Self.result(for: outcome, now: now) } private static func readOutcome(at url: URL) -> ModelsDevCacheMemo.Outcome { guard let data = try? Data(contentsOf: url) else { return .failure(.unreadable) } let decoder = JSONDecoder() decoder.dateDecodingStrategy = .iso8601 guard let decoded = try? decoder.decode(ModelsDevCacheArtifact.self, from: data) else { return .failure(.invalidJSON) } guard decoded.version == Self.artifactVersion else { return .failure(.invalidVersion) } return .decoded(decoded) } private static func result(for outcome: ModelsDevCacheMemo.Outcome, now: Date) -> ModelsDevCacheLoadResult { switch outcome { case let .decoded(artifact): ModelsDevCacheLoadResult( artifact: artifact, isStale: now.timeIntervalSince(artifact.fetchedAt) > Self.ttlSeconds, error: nil) case let .failure(error): ModelsDevCacheLoadResult(artifact: nil, isStale: true, error: error) } }