feat(coding-agent): show cache hit rate in footer
This commit is contained in:
@@ -2,6 +2,10 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- Added the latest prompt cache hit rate to the interactive footer.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed built-in tool expand hints to style closing parentheses consistently ([#5359](https://github.com/earendil-works/pi/issues/5359)).
|
||||
|
||||
@@ -155,7 +155,7 @@ The interface from top to bottom:
|
||||
- **Startup header** - Shows shortcuts (`/hotkeys` for all), loaded AGENTS.md files, prompt templates, skills, and extensions
|
||||
- **Messages** - Your messages, assistant responses, tool calls and results, notifications, errors, and extension UI
|
||||
- **Editor** - Where you type; border color indicates thinking level
|
||||
- **Footer** - Working directory, session name, total token/cache usage, cost, context usage, current model
|
||||
- **Footer** - Working directory, session name, total token/cache usage (`↑` input, `↓` output, `R` cache read, `W` cache write, `CH` latest cache hit rate), cost, context usage, current model
|
||||
|
||||
The editor can be temporarily replaced by other UI, like built-in `/settings` or custom UI from extensions (e.g., a Q&A tool that lets the user answer model questions in a structured format). [Extensions](#extensions) can also replace the editor, add widgets above/below it, a status line, custom footer, or overlays.
|
||||
|
||||
|
||||
@@ -88,6 +88,7 @@ export class FooterComponent implements Component {
|
||||
let totalCacheRead = 0;
|
||||
let totalCacheWrite = 0;
|
||||
let totalCost = 0;
|
||||
let latestCacheHitRate: number | undefined;
|
||||
|
||||
for (const entry of this.session.sessionManager.getEntries()) {
|
||||
if (entry.type === "message" && entry.message.role === "assistant") {
|
||||
@@ -96,6 +97,11 @@ export class FooterComponent implements Component {
|
||||
totalCacheRead += entry.message.usage.cacheRead;
|
||||
totalCacheWrite += entry.message.usage.cacheWrite;
|
||||
totalCost += entry.message.usage.cost.total;
|
||||
|
||||
const latestPromptTokens =
|
||||
entry.message.usage.input + entry.message.usage.cacheRead + entry.message.usage.cacheWrite;
|
||||
latestCacheHitRate =
|
||||
latestPromptTokens > 0 ? (entry.message.usage.cacheRead / latestPromptTokens) * 100 : undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,6 +133,9 @@ export class FooterComponent implements Component {
|
||||
if (totalOutput) statsParts.push(`↓${formatTokens(totalOutput)}`);
|
||||
if (totalCacheRead) statsParts.push(`R${formatTokens(totalCacheRead)}`);
|
||||
if (totalCacheWrite) statsParts.push(`W${formatTokens(totalCacheWrite)}`);
|
||||
if ((totalCacheRead > 0 || totalCacheWrite > 0) && latestCacheHitRate !== undefined) {
|
||||
statsParts.push(`CH${latestCacheHitRate.toFixed(1)}%`);
|
||||
}
|
||||
|
||||
// Show cost with "(sub)" indicator if using OAuth subscription
|
||||
const usingSubscription = state.model ? this.session.modelRegistry.isUsingOAuth(state.model) : false;
|
||||
|
||||
@@ -4,6 +4,7 @@ import type { AgentSession } from "../src/core/agent-session.ts";
|
||||
import type { ReadonlyFooterDataProvider } from "../src/core/footer-data-provider.ts";
|
||||
import { FooterComponent, formatCwdForFooter } from "../src/modes/interactive/components/footer.ts";
|
||||
import { initTheme } from "../src/modes/interactive/theme/theme.ts";
|
||||
import { stripAnsi } from "../src/utils/ansi.ts";
|
||||
|
||||
type AssistantUsage = {
|
||||
input: number;
|
||||
@@ -123,4 +124,21 @@ describe("FooterComponent width handling", () => {
|
||||
expect(visibleWidth(line)).toBeLessThanOrEqual(width);
|
||||
}
|
||||
});
|
||||
|
||||
it("shows the latest cache hit rate when cache usage is present", () => {
|
||||
const session = createSession({
|
||||
sessionName: "",
|
||||
usage: {
|
||||
input: 100,
|
||||
output: 10,
|
||||
cacheRead: 50,
|
||||
cacheWrite: 50,
|
||||
cost: { total: 0.001 },
|
||||
},
|
||||
});
|
||||
const footer = new FooterComponent(session, createFooterData(1));
|
||||
|
||||
const statsLine = stripAnsi(footer.render(120)[1]);
|
||||
expect(statsLine).toContain("CH25.0%");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user