mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 10:11:24 +00:00
fix: /status shows incorrect context percentage — totalTokens clamped to contextTokens (#15114) (#15133)
Merged via /review-pr -> /prepare-pr -> /merge-pr.
Prepared head SHA: a489669fc7
Co-authored-by: echoVic <16428813+echoVic@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
This commit is contained in:
@@ -264,6 +264,7 @@ export const sessionsHandlers: GatewayRequestHandlers = {
|
||||
inputTokens: 0,
|
||||
outputTokens: 0,
|
||||
totalTokens: 0,
|
||||
totalTokensFresh: true,
|
||||
};
|
||||
store[primaryKey] = nextEntry;
|
||||
return nextEntry;
|
||||
@@ -464,6 +465,7 @@ export const sessionsHandlers: GatewayRequestHandlers = {
|
||||
delete entryToUpdate.inputTokens;
|
||||
delete entryToUpdate.outputTokens;
|
||||
delete entryToUpdate.totalTokens;
|
||||
delete entryToUpdate.totalTokensFresh;
|
||||
entryToUpdate.updatedAt = Date.now();
|
||||
});
|
||||
|
||||
|
||||
@@ -157,6 +157,7 @@ describe("gateway server sessions", () => {
|
||||
sessions: Array<{
|
||||
key: string;
|
||||
totalTokens?: number;
|
||||
totalTokensFresh?: boolean;
|
||||
thinkingLevel?: string;
|
||||
verboseLevel?: string;
|
||||
lastAccountId?: string;
|
||||
@@ -169,7 +170,8 @@ describe("gateway server sessions", () => {
|
||||
expect(list1.payload?.sessions.some((s) => s.key === "global")).toBe(false);
|
||||
expect(list1.payload?.defaults?.modelProvider).toBe(DEFAULT_PROVIDER);
|
||||
const main = list1.payload?.sessions.find((s) => s.key === "agent:main:main");
|
||||
expect(main?.totalTokens).toBe(30);
|
||||
expect(main?.totalTokens).toBeUndefined();
|
||||
expect(main?.totalTokensFresh).toBe(false);
|
||||
expect(main?.thinkingLevel).toBe("low");
|
||||
expect(main?.verboseLevel).toBe("on");
|
||||
expect(main?.lastAccountId).toBe("work");
|
||||
|
||||
@@ -356,4 +356,45 @@ describe("listSessionsFromStore search", () => {
|
||||
|
||||
expect(result.sessions.map((session) => session.key)).toEqual(["agent:main:cron:job-1"]);
|
||||
});
|
||||
|
||||
test("exposes unknown totals when freshness is stale or missing", () => {
|
||||
const now = Date.now();
|
||||
const store: Record<string, SessionEntry> = {
|
||||
"agent:main:fresh": {
|
||||
sessionId: "sess-fresh",
|
||||
updatedAt: now,
|
||||
totalTokens: 1200,
|
||||
totalTokensFresh: true,
|
||||
} as SessionEntry,
|
||||
"agent:main:stale": {
|
||||
sessionId: "sess-stale",
|
||||
updatedAt: now - 1000,
|
||||
totalTokens: 2200,
|
||||
totalTokensFresh: false,
|
||||
} as SessionEntry,
|
||||
"agent:main:missing": {
|
||||
sessionId: "sess-missing",
|
||||
updatedAt: now - 2000,
|
||||
inputTokens: 100,
|
||||
outputTokens: 200,
|
||||
} as SessionEntry,
|
||||
};
|
||||
|
||||
const result = listSessionsFromStore({
|
||||
cfg: baseCfg,
|
||||
storePath: "/tmp/sessions.json",
|
||||
store,
|
||||
opts: {},
|
||||
});
|
||||
|
||||
const fresh = result.sessions.find((row) => row.key === "agent:main:fresh");
|
||||
const stale = result.sessions.find((row) => row.key === "agent:main:stale");
|
||||
const missing = result.sessions.find((row) => row.key === "agent:main:missing");
|
||||
expect(fresh?.totalTokens).toBe(1200);
|
||||
expect(fresh?.totalTokensFresh).toBe(true);
|
||||
expect(stale?.totalTokens).toBeUndefined();
|
||||
expect(stale?.totalTokensFresh).toBe(false);
|
||||
expect(missing?.totalTokens).toBeUndefined();
|
||||
expect(missing?.totalTokensFresh).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
buildGroupDisplayName,
|
||||
canonicalizeMainSessionAlias,
|
||||
loadSessionStore,
|
||||
resolveFreshSessionTotalTokens,
|
||||
resolveMainSessionKey,
|
||||
resolveStorePath,
|
||||
type SessionEntry,
|
||||
@@ -607,9 +608,9 @@ export function listSessionsFromStore(params: {
|
||||
})
|
||||
.map(([key, entry]) => {
|
||||
const updatedAt = entry?.updatedAt ?? null;
|
||||
const input = entry?.inputTokens ?? 0;
|
||||
const output = entry?.outputTokens ?? 0;
|
||||
const total = entry?.totalTokens ?? input + output;
|
||||
const total = resolveFreshSessionTotalTokens(entry);
|
||||
const totalTokensFresh =
|
||||
typeof entry?.totalTokens === "number" ? entry?.totalTokensFresh !== false : false;
|
||||
const parsed = parseGroupKey(key);
|
||||
const channel = entry?.channel ?? parsed?.channel;
|
||||
const subject = entry?.subject;
|
||||
@@ -662,6 +663,7 @@ export function listSessionsFromStore(params: {
|
||||
inputTokens: entry?.inputTokens,
|
||||
outputTokens: entry?.outputTokens,
|
||||
totalTokens: total,
|
||||
totalTokensFresh,
|
||||
responseUsage: entry?.responseUsage,
|
||||
modelProvider,
|
||||
model,
|
||||
|
||||
@@ -33,6 +33,7 @@ export type GatewaySessionRow = {
|
||||
inputTokens?: number;
|
||||
outputTokens?: number;
|
||||
totalTokens?: number;
|
||||
totalTokensFresh?: boolean;
|
||||
responseUsage?: "on" | "off" | "tokens" | "full";
|
||||
modelProvider?: string;
|
||||
model?: string;
|
||||
|
||||
Reference in New Issue
Block a user