fix(mac): show cached context usage
This commit is contained in:
@@ -31,9 +31,10 @@ struct ContextUsageBar: View {
|
||||
.fill(Color.secondary.opacity(0.25))
|
||||
Capsule()
|
||||
.fill(self.tint)
|
||||
.frame(width: fillWidth)
|
||||
.frame(width: max(1, fillWidth))
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.frame(height: self.height)
|
||||
.accessibilityLabel("Context usage")
|
||||
.accessibilityValue(self.accessibilityValue)
|
||||
@@ -46,4 +47,3 @@ struct ContextUsageBar: View {
|
||||
return "\(pct) percent used"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -266,6 +266,7 @@ struct MenuContent: View {
|
||||
ContextUsageBar(
|
||||
usedTokens: row.tokens.total,
|
||||
contextTokens: row.tokens.contextTokens)
|
||||
.frame(width: 220)
|
||||
}
|
||||
.padding(.vertical, 2)
|
||||
} else {
|
||||
|
||||
@@ -270,11 +270,15 @@ enum SessionLoader {
|
||||
throw SessionLoadError.decodeFailed(error.localizedDescription)
|
||||
}
|
||||
|
||||
let storeDir = URL(fileURLWithPath: path).deletingLastPathComponent()
|
||||
|
||||
return decoded.map { key, entry in
|
||||
let updated = entry.updatedAt.map { Date(timeIntervalSince1970: $0 / 1000) }
|
||||
let input = entry.inputTokens ?? 0
|
||||
let output = entry.outputTokens ?? 0
|
||||
let total = entry.totalTokens ?? input + output
|
||||
let fallbackTotal = entry.totalTokens ?? input + output
|
||||
let promptTokens = entry.sessionId.flatMap { self.promptTokensFromSessionLog(sessionId: $0, storeDir: storeDir) }
|
||||
let total = max(fallbackTotal, promptTokens ?? 0)
|
||||
let context = entry.contextTokens ?? defaults.contextTokens
|
||||
let model = entry.model ?? defaults.model
|
||||
|
||||
@@ -299,6 +303,67 @@ enum SessionLoader {
|
||||
}.value
|
||||
}
|
||||
|
||||
private static func promptTokensFromSessionLog(sessionId: String, storeDir: URL) -> Int? {
|
||||
let trimmed = sessionId.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard !trimmed.isEmpty else { return nil }
|
||||
|
||||
let candidates: [URL] = [
|
||||
storeDir.appendingPathComponent("\(trimmed).jsonl"),
|
||||
FileManager.default.homeDirectoryForCurrentUser
|
||||
.appendingPathComponent(".pi/agent/sessions")
|
||||
.appendingPathComponent("\(trimmed).jsonl"),
|
||||
FileManager.default.homeDirectoryForCurrentUser
|
||||
.appendingPathComponent(".tau/agent/sessions/clawdis")
|
||||
.appendingPathComponent("\(trimmed).jsonl"),
|
||||
]
|
||||
|
||||
guard let logURL = candidates.first(where: { FileManager.default.fileExists(atPath: $0.path) }) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let text = try? String(contentsOf: logURL, encoding: .utf8) else { return nil }
|
||||
var lastUsage: [String: Any]?
|
||||
|
||||
for line in text.split(whereSeparator: \.isNewline) {
|
||||
let trimmedLine = line.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
if trimmedLine.isEmpty { continue }
|
||||
guard let data = trimmedLine.data(using: .utf8) else { continue }
|
||||
guard let obj = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else { continue }
|
||||
|
||||
if let message = obj["message"] as? [String: Any], let usage = message["usage"] as? [String: Any] {
|
||||
lastUsage = usage
|
||||
continue
|
||||
}
|
||||
if let usage = obj["usage"] as? [String: Any] {
|
||||
lastUsage = usage
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
guard let lastUsage else { return nil }
|
||||
|
||||
let input = self.number(from: lastUsage["input"]) ?? 0
|
||||
let output = self.number(from: lastUsage["output"]) ?? 0
|
||||
let cacheRead = self.number(from: lastUsage["cacheRead"] ?? lastUsage["cache_read"]) ?? 0
|
||||
let cacheWrite = self.number(from: lastUsage["cacheWrite"] ?? lastUsage["cache_write"]) ?? 0
|
||||
let totalTokens = self.number(from: lastUsage["totalTokens"] ?? lastUsage["total_tokens"] ?? lastUsage["total"])
|
||||
|
||||
let prompt = input + cacheRead + cacheWrite
|
||||
if prompt > 0 { return prompt }
|
||||
if let totalTokens, totalTokens > output { return totalTokens - output }
|
||||
return nil
|
||||
}
|
||||
|
||||
private static func number(from raw: Any?) -> Int? {
|
||||
switch raw {
|
||||
case let v as Int: v
|
||||
case let v as Double: Int(v)
|
||||
case let v as NSNumber: v.intValue
|
||||
case let v as String: Int(v)
|
||||
default: nil
|
||||
}
|
||||
}
|
||||
|
||||
private static func standardize(_ path: String) -> String {
|
||||
(path as NSString).expandingTildeInPath.replacingOccurrences(of: "//", with: "/")
|
||||
}
|
||||
|
||||
@@ -152,6 +152,7 @@ struct SessionsSettings: View {
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
ContextUsageBar(usedTokens: row.tokens.total, contextTokens: row.tokens.contextTokens)
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
|
||||
HStack(spacing: 10) {
|
||||
|
||||
Reference in New Issue
Block a user