fix(worker): record real raw token usage per run, not the uncached remainder
task_runs.tokens_in only ever held the API's uncached "input_tokens" field (off by a factor of ~400,000 on a resumed session), and tokens_out summed only the last result event instead of the whole session. TaskRunner now reads each run's cache-read/cache-write/input/output totals from the session transcript via a new ITranscriptUsageReader.ReadSessionTotalsAsync, storing the delta against prior runs on the same session so a --resume doesn't double-count. New task_runs.cache_read_tokens/cache_write_tokens columns; the Session tab now shows the raw total (what actually counts against the 5h/7d usage limit) with a breakdown tooltip.
This commit is contained in:
@@ -62,6 +62,33 @@ public sealed class TranscriptUsageReader : ITranscriptUsageReader
|
||||
return Task.FromResult<IReadOnlyList<UsageAggregateRow>>(rows);
|
||||
}
|
||||
|
||||
public Task<SessionUsageTotals?> ReadSessionTotalsAsync(string sessionId, CancellationToken ct = default)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(sessionId) || !Directory.Exists(_projectsRoot))
|
||||
return Task.FromResult<SessionUsageTotals?>(null);
|
||||
|
||||
var file = Directory
|
||||
.EnumerateFiles(_projectsRoot, $"{sessionId}.jsonl", SearchOption.AllDirectories)
|
||||
.FirstOrDefault();
|
||||
if (file is null) return Task.FromResult<SessionUsageTotals?>(null);
|
||||
|
||||
var seenKeys = new HashSet<string>();
|
||||
long input = 0, output = 0, cacheRead = 0, cacheCreation = 0;
|
||||
foreach (var record in GetOrReadFile(file))
|
||||
{
|
||||
ct.ThrowIfCancellationRequested();
|
||||
if (!seenKeys.Add(record.DedupeKey)) continue;
|
||||
|
||||
input += record.InputTokens;
|
||||
output += record.OutputTokens;
|
||||
cacheRead += record.CacheReadTokens;
|
||||
cacheCreation += record.CacheCreationTokens;
|
||||
}
|
||||
|
||||
return Task.FromResult<SessionUsageTotals?>(
|
||||
new SessionUsageTotals(input, output, cacheRead, cacheCreation));
|
||||
}
|
||||
|
||||
private List<UsageMessageRecord> GetOrReadFile(string file)
|
||||
{
|
||||
var info = new FileInfo(file);
|
||||
@@ -100,6 +127,7 @@ public sealed class TranscriptUsageReader : ITranscriptUsageReader
|
||||
|
||||
var date = DateOnly.FromDateTime(ts.LocalDateTime);
|
||||
var model = modelEl.GetString()!;
|
||||
if (model == "<synthetic>") continue;
|
||||
|
||||
long input = 0, output = 0, cacheRead = 0, cacheCreation = 0;
|
||||
if (msg.TryGetProperty("usage", out var usage) && usage.ValueKind == JsonValueKind.Object)
|
||||
|
||||
Reference in New Issue
Block a user