mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 02:21:25 +00:00
fix: update totalTokens after compaction using last-call usage (#15018)
Merged via /review-pr -> /prepare-pr -> /merge-pr.
Prepared head SHA: 9214291bf7
Co-authored-by: shtse8 <8020099+shtse8@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
This commit is contained in:
95
src/auto-reply/reply/session-usage.test.ts
Normal file
95
src/auto-reply/reply/session-usage.test.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { persistSessionUsageUpdate } from "./session-usage.js";
|
||||
|
||||
async function seedSessionStore(params: {
|
||||
storePath: string;
|
||||
sessionKey: string;
|
||||
entry: Record<string, unknown>;
|
||||
}) {
|
||||
await fs.mkdir(path.dirname(params.storePath), { recursive: true });
|
||||
await fs.writeFile(
|
||||
params.storePath,
|
||||
JSON.stringify({ [params.sessionKey]: params.entry }, null, 2),
|
||||
"utf-8",
|
||||
);
|
||||
}
|
||||
|
||||
describe("persistSessionUsageUpdate", () => {
|
||||
it("uses lastCallUsage for totalTokens when provided", async () => {
|
||||
const tmp = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-usage-"));
|
||||
const storePath = path.join(tmp, "sessions.json");
|
||||
const sessionKey = "main";
|
||||
await seedSessionStore({
|
||||
storePath,
|
||||
sessionKey,
|
||||
entry: { sessionId: "s1", updatedAt: Date.now(), totalTokens: 100_000 },
|
||||
});
|
||||
|
||||
// Accumulated usage (sums all API calls) — inflated
|
||||
const accumulatedUsage = { input: 180_000, output: 10_000, total: 190_000 };
|
||||
// Last individual API call's usage — actual context after compaction
|
||||
const lastCallUsage = { input: 12_000, output: 2_000, total: 14_000 };
|
||||
|
||||
await persistSessionUsageUpdate({
|
||||
storePath,
|
||||
sessionKey,
|
||||
usage: accumulatedUsage,
|
||||
lastCallUsage,
|
||||
contextTokensUsed: 200_000,
|
||||
});
|
||||
|
||||
const stored = JSON.parse(await fs.readFile(storePath, "utf-8"));
|
||||
// totalTokens should reflect lastCallUsage (12_000 input), not accumulated (180_000)
|
||||
expect(stored[sessionKey].totalTokens).toBe(12_000);
|
||||
// inputTokens/outputTokens still reflect accumulated usage for cost tracking
|
||||
expect(stored[sessionKey].inputTokens).toBe(180_000);
|
||||
expect(stored[sessionKey].outputTokens).toBe(10_000);
|
||||
});
|
||||
|
||||
it("falls back to accumulated usage for totalTokens when lastCallUsage not provided", async () => {
|
||||
const tmp = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-usage-"));
|
||||
const storePath = path.join(tmp, "sessions.json");
|
||||
const sessionKey = "main";
|
||||
await seedSessionStore({
|
||||
storePath,
|
||||
sessionKey,
|
||||
entry: { sessionId: "s1", updatedAt: Date.now() },
|
||||
});
|
||||
|
||||
await persistSessionUsageUpdate({
|
||||
storePath,
|
||||
sessionKey,
|
||||
usage: { input: 50_000, output: 5_000, total: 55_000 },
|
||||
contextTokensUsed: 200_000,
|
||||
});
|
||||
|
||||
const stored = JSON.parse(await fs.readFile(storePath, "utf-8"));
|
||||
expect(stored[sessionKey].totalTokens).toBe(50_000);
|
||||
});
|
||||
|
||||
it("caps totalTokens at context window even with lastCallUsage", async () => {
|
||||
const tmp = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-usage-"));
|
||||
const storePath = path.join(tmp, "sessions.json");
|
||||
const sessionKey = "main";
|
||||
await seedSessionStore({
|
||||
storePath,
|
||||
sessionKey,
|
||||
entry: { sessionId: "s1", updatedAt: Date.now() },
|
||||
});
|
||||
|
||||
await persistSessionUsageUpdate({
|
||||
storePath,
|
||||
sessionKey,
|
||||
usage: { input: 300_000, output: 10_000, total: 310_000 },
|
||||
lastCallUsage: { input: 250_000, output: 5_000, total: 255_000 },
|
||||
contextTokensUsed: 200_000,
|
||||
});
|
||||
|
||||
const stored = JSON.parse(await fs.readFile(storePath, "utf-8"));
|
||||
// Capped at context window
|
||||
expect(stored[sessionKey].totalTokens).toBe(200_000);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user