mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 00:21:23 +00:00
feat: surface cached token counts in /status output (openclaw#21248) thanks @vishaltandale00
Verified: - pnpm build - pnpm check - pnpm test:macmini Co-authored-by: vishaltandale00 <9222298+vishaltandale00@users.noreply.github.com> Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
This commit is contained in:
@@ -76,6 +76,8 @@ export async function updateSessionStoreAfterAgentRun(params: {
|
||||
next.outputTokens = output;
|
||||
next.totalTokens = totalTokens;
|
||||
next.totalTokensFresh = true;
|
||||
next.cacheRead = usage.cacheRead ?? 0;
|
||||
next.cacheWrite = usage.cacheWrite ?? 0;
|
||||
}
|
||||
if (compactionsThisRun > 0) {
|
||||
next.compactionCount = (entry.compactionCount ?? 0) + compactionsThisRun;
|
||||
|
||||
@@ -20,6 +20,8 @@ function createDefaultSessionStoreEntry() {
|
||||
thinkingLevel: "low",
|
||||
inputTokens: 2_000,
|
||||
outputTokens: 3_000,
|
||||
cacheRead: 2_000,
|
||||
cacheWrite: 1_000,
|
||||
totalTokens: 5_000,
|
||||
contextTokens: 10_000,
|
||||
model: "pi:opus",
|
||||
@@ -340,6 +342,8 @@ describe("statusCommand", () => {
|
||||
expect(payload.sessions.defaults.model).toBeTruthy();
|
||||
expect(payload.sessions.defaults.contextTokens).toBeGreaterThan(0);
|
||||
expect(payload.sessions.recent[0].percentUsed).toBe(50);
|
||||
expect(payload.sessions.recent[0].cacheRead).toBe(2_000);
|
||||
expect(payload.sessions.recent[0].cacheWrite).toBe(1_000);
|
||||
expect(payload.sessions.recent[0].totalTokensFresh).toBe(true);
|
||||
expect(payload.sessions.recent[0].remainingTokens).toBe(5000);
|
||||
expect(payload.sessions.recent[0].flags).toContain("verbose:on");
|
||||
@@ -387,6 +391,7 @@ describe("statusCommand", () => {
|
||||
expect(logs.some((l: string) => l.includes("Sessions"))).toBe(true);
|
||||
expect(logs.some((l: string) => l.includes("+1000"))).toBe(true);
|
||||
expect(logs.some((l: string) => l.includes("50%"))).toBe(true);
|
||||
expect(logs.some((l: string) => l.includes("40% cached"))).toBe(true);
|
||||
expect(logs.some((l: string) => l.includes("LaunchAgent"))).toBe(true);
|
||||
expect(logs.some((l: string) => l.includes("FAQ:"))).toBe(true);
|
||||
expect(logs.some((l: string) => l.includes("Troubleshooting:"))).toBe(true);
|
||||
|
||||
@@ -21,18 +21,37 @@ export const shortenText = (value: string, maxLen: number) => {
|
||||
};
|
||||
|
||||
export const formatTokensCompact = (
|
||||
sess: Pick<SessionStatus, "totalTokens" | "contextTokens" | "percentUsed">,
|
||||
sess: Pick<
|
||||
SessionStatus,
|
||||
"totalTokens" | "contextTokens" | "percentUsed" | "cacheRead" | "cacheWrite"
|
||||
>,
|
||||
) => {
|
||||
const used = sess.totalTokens;
|
||||
const ctx = sess.contextTokens;
|
||||
const cacheRead = sess.cacheRead;
|
||||
const cacheWrite = sess.cacheWrite;
|
||||
|
||||
let result = "";
|
||||
if (used == null) {
|
||||
return ctx ? `unknown/${formatKTokens(ctx)} (?%)` : "unknown used";
|
||||
result = ctx ? `unknown/${formatKTokens(ctx)} (?%)` : "unknown used";
|
||||
} else if (!ctx) {
|
||||
result = `${formatKTokens(used)} used`;
|
||||
} else {
|
||||
const pctLabel = sess.percentUsed != null ? `${sess.percentUsed}%` : "?%";
|
||||
result = `${formatKTokens(used)}/${formatKTokens(ctx)} (${pctLabel})`;
|
||||
}
|
||||
if (!ctx) {
|
||||
return `${formatKTokens(used)} used`;
|
||||
|
||||
// Add cache hit rate if there are cached reads
|
||||
if (typeof cacheRead === "number" && cacheRead > 0) {
|
||||
const total =
|
||||
typeof used === "number"
|
||||
? used
|
||||
: cacheRead + (typeof cacheWrite === "number" ? cacheWrite : 0);
|
||||
const hitRate = Math.round((cacheRead / total) * 100);
|
||||
result += ` · 🗄️ ${hitRate}% cached`;
|
||||
}
|
||||
const pctLabel = sess.percentUsed != null ? `${sess.percentUsed}%` : "?%";
|
||||
return `${formatKTokens(used)}/${formatKTokens(ctx)} (${pctLabel})`;
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
export const formatDaemonRuntimeShort = (runtime?: {
|
||||
|
||||
@@ -160,6 +160,8 @@ export async function getStatusSummary(
|
||||
abortedLastRun: entry?.abortedLastRun,
|
||||
inputTokens: entry?.inputTokens,
|
||||
outputTokens: entry?.outputTokens,
|
||||
cacheRead: entry?.cacheRead,
|
||||
cacheWrite: entry?.cacheWrite,
|
||||
totalTokens: total ?? null,
|
||||
totalTokensFresh,
|
||||
remainingTokens: remaining,
|
||||
|
||||
@@ -17,6 +17,8 @@ export type SessionStatus = {
|
||||
outputTokens?: number;
|
||||
totalTokens: number | null;
|
||||
totalTokensFresh: boolean;
|
||||
cacheRead?: number;
|
||||
cacheWrite?: number;
|
||||
remainingTokens: number | null;
|
||||
percentUsed: number | null;
|
||||
model: string | null;
|
||||
|
||||
Reference in New Issue
Block a user