all: only display total input tokens
Knowing the cache reads and writes was mildly interesting for us,
but it is inside baseball, and our calculations ended up being a little wrong.
Streamline the UI and make the math right.
Co-Authored-By: sketch <hello@sketch.dev>
diff --git a/ant/ant.go b/ant/ant.go
index 2ba714e..463d5fb 100644
--- a/ant/ant.go
+++ b/ant/ant.go
@@ -908,6 +908,11 @@
u.TotalCostUSD += resp.TotalDollars()
}
+// TotalInputTokens returns the grand total cumulative input tokens in u.
+func (u *CumulativeUsage) TotalInputTokens() uint64 {
+ return u.InputTokens + u.CacheReadInputTokens + u.CacheCreationInputTokens
+}
+
// Attr returns the cumulative usage as a slog.Attr with key "usage".
func (u CumulativeUsage) Attr() slog.Attr {
elapsed := time.Since(u.StartTime)
diff --git a/termui/termui.go b/termui/termui.go
index 0342a5e..32f3300 100644
--- a/termui/termui.go
+++ b/termui/termui.go
@@ -206,7 +206,7 @@
case "usage", "cost":
totalUsage := ui.agent.TotalUsage()
ui.AppendSystemMessage("💰 Current usage summary:")
- ui.AppendSystemMessage("- Input tokens: %d [Cache: read=%d, creation=%d]", totalUsage.InputTokens, totalUsage.CacheReadInputTokens, totalUsage.CacheCreationInputTokens)
+ ui.AppendSystemMessage("- Input tokens: %d", totalUsage.TotalInputTokens())
ui.AppendSystemMessage("- Output tokens: %d", totalUsage.OutputTokens)
ui.AppendSystemMessage("- Responses: %d", totalUsage.Responses)
ui.AppendSystemMessage("- Wall time: %s", totalUsage.WallTime().Round(time.Second))
@@ -216,7 +216,7 @@
// Display final usage stats
totalUsage := ui.agent.TotalUsage()
ui.AppendSystemMessage("💰 Final usage summary:")
- ui.AppendSystemMessage("- Input tokens: %d [Cache: read=%d, creation=%d]", totalUsage.InputTokens, totalUsage.CacheReadInputTokens, totalUsage.CacheCreationInputTokens)
+ ui.AppendSystemMessage("- Input tokens: %d", totalUsage.TotalInputTokens())
ui.AppendSystemMessage("- Output tokens: %d", totalUsage.OutputTokens)
ui.AppendSystemMessage("- Responses: %d", totalUsage.Responses)
ui.AppendSystemMessage("- Wall time: %s", totalUsage.WallTime().Round(time.Second))
diff --git a/webui/src/web-components/sketch-container-status.test.ts b/webui/src/web-components/sketch-container-status.test.ts
index db11a4e..b7dfd01 100644
--- a/webui/src/web-components/sketch-container-status.test.ts
+++ b/webui/src/web-components/sketch-container-status.test.ts
@@ -42,19 +42,16 @@
await expect(component.locator("#messageCount")).toContainText(
mockCompleteState.message_count + "",
);
+ const expectedTotalInputTokens =
+ mockCompleteState.total_usage.input_tokens +
+ mockCompleteState.total_usage.cache_read_input_tokens +
+ mockCompleteState.total_usage.cache_creation_input_tokens;
await expect(component.locator("#inputTokens")).toContainText(
- mockCompleteState.total_usage.input_tokens + "",
+ expectedTotalInputTokens + "",
);
await expect(component.locator("#outputTokens")).toContainText(
mockCompleteState.total_usage.output_tokens + "",
);
-
- await expect(component.locator("#cacheReadInputTokens")).toContainText(
- mockCompleteState.total_usage.cache_read_input_tokens + "",
- );
- await expect(component.locator("#cacheCreationInputTokens")).toContainText(
- mockCompleteState.total_usage.cache_creation_input_tokens + "",
- );
await expect(component.locator("#totalCost")).toContainText(
"$" + mockCompleteState.total_usage.total_cost_usd.toFixed(2),
);
@@ -68,7 +65,7 @@
await expect(component.locator("#workingDir")).toContainText("");
await expect(component.locator("#initialCommit")).toContainText("");
await expect(component.locator("#messageCount")).toContainText("");
- await expect(component.locator("#inputTokens")).toContainText("");
+ await expect(component.locator("#inputTokens")).toContainText("0");
await expect(component.locator("#outputTokens")).toContainText("");
await expect(component.locator("#totalCost")).toContainText("$0.00");
});
@@ -100,12 +97,19 @@
// Check that elements with data are properly populated
await expect(component.locator("#hostname")).toContainText("partial-host");
await expect(component.locator("#messageCount")).toContainText("10");
- await expect(component.locator("#inputTokens")).toContainText("500");
+
+ const expectedTotalInputTokens =
+ partialState.total_usage.input_tokens +
+ partialState.total_usage.cache_read_input_tokens +
+ partialState.total_usage.cache_creation_input_tokens;
+ await expect(component.locator("#inputTokens")).toContainText(
+ expectedTotalInputTokens + "",
+ );
// Check that elements without data are empty
await expect(component.locator("#workingDir")).toContainText("");
await expect(component.locator("#initialCommit")).toContainText("");
- await expect(component.locator("#outputTokens")).toContainText("");
+ await expect(component.locator("#outputTokens")).toContainText("0");
await expect(component.locator("#totalCost")).toContainText("$0.00");
});
diff --git a/webui/src/web-components/sketch-container-status.ts b/webui/src/web-components/sketch-container-status.ts
index 9e542cb..8079d2e 100644
--- a/webui/src/web-components/sketch-container-status.ts
+++ b/webui/src/web-components/sketch-container-status.ts
@@ -196,25 +196,15 @@
>
</div>
<div class="info-item">
- <span class="info-label">In:</span>
+ <span class="info-label">Input tokens:</span>
<span id="inputTokens" class="info-value"
- >${this.state?.total_usage?.input_tokens}</span
+ >${(this.state?.total_usage?.input_tokens || 0) +
+ (this.state?.total_usage?.cache_read_input_tokens || 0) +
+ (this.state?.total_usage?.cache_creation_input_tokens || 0)}</span
>
</div>
<div class="info-item">
- <span class="info-label">Cache Read:</span>
- <span id="cacheReadInputTokens" class="info-value"
- >${this.state?.total_usage?.cache_read_input_tokens}</span
- >
- </div>
- <div class="info-item">
- <span class="info-label">Cache Create:</span>
- <span id="cacheCreationInputTokens" class="info-value"
- >${this.state?.total_usage?.cache_creation_input_tokens}</span
- >
- </div>
- <div class="info-item">
- <span class="info-label">Out:</span>
+ <span class="info-label">Output tokens:</span>
<span id="outputTokens" class="info-value"
>${this.state?.total_usage?.output_tokens}</span
>
diff --git a/webui/src/web-components/sketch-timeline-message.test.ts b/webui/src/web-components/sketch-timeline-message.test.ts
index bc74202..d5c8dfb 100644
--- a/webui/src/web-components/sketch-timeline-message.test.ts
+++ b/webui/src/web-components/sketch-timeline-message.test.ts
@@ -149,9 +149,8 @@
});
await expect(component.locator(".message-usage")).toBeVisible();
- await expect(component.locator(".message-usage")).toContainText("150"); // In
+ await expect(component.locator(".message-usage")).toContainText("200"); // In (150 + 50 cache)
await expect(component.locator(".message-usage")).toContainText("300"); // Out
- await expect(component.locator(".message-usage")).toContainText("50"); // Cache
await expect(component.locator(".message-usage")).toContainText("$0.03"); // Cost
});
diff --git a/webui/src/web-components/sketch-timeline-message.ts b/webui/src/web-components/sketch-timeline-message.ts
index 36f1640..a2dd533 100644
--- a/webui/src/web-components/sketch-timeline-message.ts
+++ b/webui/src/web-components/sketch-timeline-message.ts
@@ -595,16 +595,12 @@
${this.message?.usage
? html` <span class="message-usage">
<span title="Input tokens"
- >In: ${this.message?.usage?.input_tokens}</span
+ >In:
+ ${(this.message?.usage?.input_tokens || 0) +
+ (this.message?.usage?.cache_read_input_tokens || 0) +
+ (this.message?.usage?.cache_creation_input_tokens ||
+ 0)}</span
>
- ${this.message?.usage?.cache_read_input_tokens > 0
- ? html`<span title="Cache tokens"
- >[Cache:
- ${this.formatNumber(
- this.message?.usage?.cache_read_input_tokens,
- )}]</span
- >`
- : ""}
<span title="Output tokens"
>Out: ${this.message?.usage?.output_tokens}</span
>