




























@@ -37,6 +37,39 @@ import { testApi, usageHandlers } from "./usage.js";
37373838describe("gateway usage helpers", () => {
3939const dayMs = 24 * 60 * 60 * 1000;
40+const costSummary = (params: { date?: string; totalTokens: number; totalCost: number }) => ({
41+updatedAt: Date.now(),
42+days: 1,
43+daily: [
44+{
45+date: params.date ?? "2026-02-01",
46+input: params.totalTokens,
47+output: 0,
48+cacheRead: 0,
49+cacheWrite: 0,
50+totalTokens: params.totalTokens,
51+totalCost: params.totalCost,
52+inputCost: params.totalCost,
53+outputCost: 0,
54+cacheReadCost: 0,
55+cacheWriteCost: 0,
56+missingCostEntries: 0,
57+},
58+],
59+totals: {
60+input: params.totalTokens,
61+output: 0,
62+cacheRead: 0,
63+cacheWrite: 0,
64+totalTokens: params.totalTokens,
65+totalCost: params.totalCost,
66+inputCost: params.totalCost,
67+outputCost: 0,
68+cacheReadCost: 0,
69+cacheWriteCost: 0,
70+missingCostEntries: 0,
71+},
72+});
40734174function expectUtcDateRange(
4275range: ReturnType<typeof testApi.parseDateRange>,
@@ -257,4 +290,64 @@ describe("gateway usage helpers", () => {
257290expect.objectContaining({ agentId: "research" }),
258291);
259292});
293+294+it("aggregates usage.cost only for explicit all-agent scope", async () => {
295+vi.mocked(loadCostUsageSummaryFromCache).mockImplementation(async (params) =>
296+params?.agentId === "opus"
297+ ? costSummary({ totalTokens: 20, totalCost: 2 })
298+ : costSummary({ totalTokens: 10, totalCost: 1 }),
299+);
300+301+const config = {
302+agents: { list: [{ id: "main" }, { id: "opus" }] },
303+session: {},
304+} as OpenClawConfig;
305+const context = { getRuntimeConfig: () => config };
306+const params = { startDate: "2026-02-01", endDate: "2026-02-01", mode: "utc" };
307+308+const defaultRespond = vi.fn();
309+await usageHandlers["usage.cost"]({
310+respond: defaultRespond,
311+ params,
312+ context,
313+} as unknown as Parameters<(typeof usageHandlers)["usage.cost"]>[0]);
314+315+expect(vi.mocked(loadCostUsageSummaryFromCache)).toHaveBeenCalledTimes(1);
316+expect(vi.mocked(loadCostUsageSummaryFromCache).mock.calls[0]?.[0]?.agentId).toBeUndefined();
317+expect(defaultRespond.mock.calls[0]?.[1]).toMatchObject({
318+totals: { totalTokens: 10, totalCost: 1 },
319+});
320+321+const aggregateRespond = vi.fn();
322+await usageHandlers["usage.cost"]({
323+respond: aggregateRespond,
324+params: { ...params, agentScope: "all" },
325+ context,
326+} as unknown as Parameters<(typeof usageHandlers)["usage.cost"]>[0]);
327+328+expect(vi.mocked(loadCostUsageSummaryFromCache)).toHaveBeenCalledTimes(3);
329+expect(
330+vi.mocked(loadCostUsageSummaryFromCache)
331+.mock.calls.slice(1)
332+.map((call) => call[0]?.agentId),
333+).toEqual(["main", "opus"]);
334+expect(aggregateRespond.mock.calls[0]?.[0]).toBe(true);
335+expect(aggregateRespond.mock.calls[0]?.[1]).toMatchObject({
336+totals: { totalTokens: 30, totalCost: 3 },
337+daily: [{ date: "2026-02-01", totalTokens: 30, totalCost: 3 }],
338+});
339+340+const mainRespond = vi.fn();
341+await usageHandlers["usage.cost"]({
342+respond: mainRespond,
343+params: { ...params, agentId: "main" },
344+ context,
345+} as unknown as Parameters<(typeof usageHandlers)["usage.cost"]>[0]);
346+347+expect(vi.mocked(loadCostUsageSummaryFromCache)).toHaveBeenCalledTimes(4);
348+expect(vi.mocked(loadCostUsageSummaryFromCache).mock.calls[3]?.[0]?.agentId).toBe("main");
349+expect(mainRespond.mock.calls[0]?.[1]).toMatchObject({
350+totals: { totalTokens: 10, totalCost: 1 },
351+});
352+});
260353});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。