fix(gateway): bind usage refresh to runtime config (#124131)

This commit is contained in:
Peter Steinberger
2026-08-15 02:40:16 -07:00
committed by GitHub
parent 03c1d7727c
commit 9fabdb40dc
3 changed files with 32 additions and 4 deletions
@@ -36,7 +36,7 @@ export type ProviderUsageStatus = Pick<
type ProviderUsageCacheEntry = {
agentDir: string;
configRef: object;
configRef: OpenClawConfig;
credentialKey: string;
providerKey: string;
refreshedAt: number;
@@ -46,7 +46,7 @@ type ProviderUsageCacheEntry = {
type ProviderUsageRefresh = {
agentDir: string;
configRef: object;
configRef: OpenClawConfig;
credentialKey: string;
providerKey: string;
promise: Promise<UsageSummary>;
@@ -158,7 +158,7 @@ function mapProviderUsage(usage: Awaited<ReturnType<typeof loadProviderUsageSumm
function scheduleProviderUsageRefresh(params: {
agentId: string;
agentDir: string;
configRef: object;
configRef: OpenClawConfig;
credentialKey: string;
providerIds: UsageProviderId[];
providerKey: string;
@@ -176,6 +176,7 @@ function scheduleProviderUsageRefresh(params: {
const promise = loadProviderUsageSummary({
providers: params.providerIds,
agentDir: params.agentDir,
config: params.configRef,
timeoutMs: 3500,
})
.then((usage) => {
@@ -222,7 +223,7 @@ function scheduleProviderUsageRefresh(params: {
type ProviderUsageCacheParams = {
agentId: string;
agentDir: string;
configRef: object;
configRef: OpenClawConfig;
credentialKey: string;
forceRefresh?: boolean;
providerIds: UsageProviderId[];
@@ -1010,6 +1010,8 @@ describe("models.authStatus", () => {
});
it("routes claude-cli OAuth profiles to Anthropic usage with plan and billing", async () => {
const runtimeConfig = {};
mocks.getRuntimeConfig.mockReturnValue(runtimeConfig);
const profile = {
profileId: "claude-cli",
provider: "claude-cli",
@@ -1044,6 +1046,7 @@ describe("models.authStatus", () => {
expect(mocks.loadProviderUsageSummary).toHaveBeenCalledWith({
providers: ["anthropic"],
agentDir: "/tmp/agent",
config: runtimeConfig,
timeoutMs: 3500,
});
let result: ModelAuthStatusResult | undefined;
@@ -1087,6 +1090,7 @@ describe("models.authStatus", () => {
expect(mocks.loadProviderUsageSummary).toHaveBeenCalledWith({
providers: ["deepseek"],
agentDir: "/tmp/agent",
config: expect.any(Object),
timeoutMs: 3500,
});
let result: ModelAuthStatusResult | undefined;
@@ -1225,6 +1229,7 @@ describe("models.authStatus", () => {
expect(mocks.loadProviderUsageSummary).toHaveBeenLastCalledWith({
providers: ["openai"],
agentDir: "/tmp/rebound-agent",
config: expect.any(Object),
timeoutMs: 3500,
});
});
@@ -114,6 +114,28 @@ describe("usage.status provider usage cache", () => {
vi.restoreAllMocks();
});
it("loads the cached provider snapshot from the exact runtime config", async () => {
mocks.loadProviderUsageSummary.mockImplementation(async (options) => ({
updatedAt: now,
providers:
options.config === config
? [
{
provider: "openai",
displayName: "OpenAI",
windows: [{ label: "5h", usedPercent: 25 }],
accountEmail: "configured@example.com",
},
]
: [],
}));
const result = (await runUsageStatus()) as {
providers: Array<{ accountEmail?: string }>;
};
expect(result.providers[0]?.accountEmail).toBe("configured@example.com");
});
it("reuses byte-identical results within 60s and refreshes stale data in the background", async () => {
const first = await runUsageStatus();
const repeated = await runUsageStatus();