Files
openclaw/src/infra/provider-usage.load.ts
litang9 d446c26acb feat(deepseek): show provider balance in usage status
Show DeepSeek API-key account balance in status/auth-status usage surfaces by adding a summary-only provider usage snapshot path, a DeepSeek balance fetcher, SDK/docs coverage, and focused regression tests.

Maintainer verification accepted the additive provider-usage/status contract and the DeepSeek balance visibility boundary for authenticated status surfaces.

Proof:
- Live DeepSeek balance proof via 1Password-backed DEEPSEEK_API_KEY against https://api.deepseek.com/user/balance; key and balance amount redacted.
- GitHub CI run 26717953383 passed on the current head.
- Real behavior proof run 26718215605 passed after the PR body was refreshed.
- Local clean PR clone: git diff --check; node --max-old-space-size=8192 --import tsx scripts/generate-plugin-sdk-api-baseline.ts --check; node scripts/run-vitest.mjs run src/agents/bash-tools.exec.path.test.ts.

Co-authored-by: Alex Tang <tangli1987118@hotmail.com>
Co-authored-by: litang9 <141409885+litang9@users.noreply.github.com>
2026-05-31 17:35:41 +01:00

152 lines
4.1 KiB
TypeScript

import { getRuntimeConfig, type OpenClawConfig } from "../config/config.js";
import { resolveProviderUsageSnapshotWithPlugin } from "../plugins/provider-runtime.js";
import { resolveFetch } from "./fetch.js";
import { type ProviderAuth, resolveProviderAuths } from "./provider-usage.auth.js";
import {
DEFAULT_TIMEOUT_MS,
ignoredErrors,
PROVIDER_LABELS,
usageProviders,
withTimeout,
} from "./provider-usage.shared.js";
import type {
ProviderUsageSnapshot,
UsageProviderId,
UsageSummary,
} from "./provider-usage.types.js";
async function fetchProviderUsageSnapshotFallback(params: {
auth: ProviderAuth;
timeoutMs: number;
fetchFn: typeof fetch;
}): Promise<ProviderUsageSnapshot> {
void params.timeoutMs;
void params.fetchFn;
return {
provider: params.auth.provider,
displayName: PROVIDER_LABELS[params.auth.provider] ?? params.auth.provider,
windows: [],
error: "Unsupported provider",
};
}
type UsageSummaryOptions = {
now?: number;
timeoutMs?: number;
providers?: UsageProviderId[];
auth?: ProviderAuth[];
agentDir?: string;
workspaceDir?: string;
config?: OpenClawConfig;
env?: NodeJS.ProcessEnv;
fetch?: typeof fetch;
skipPluginAuthWithoutCredentialSource?: boolean;
};
async function fetchProviderUsageSnapshot(params: {
auth: ProviderAuth;
config: OpenClawConfig;
env: NodeJS.ProcessEnv;
agentDir?: string;
workspaceDir?: string;
timeoutMs: number;
fetchFn: typeof fetch;
}): Promise<ProviderUsageSnapshot> {
const pluginSnapshot = await resolveProviderUsageSnapshotWithPlugin({
provider: params.auth.provider,
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
context: {
config: params.config,
agentDir: params.agentDir,
workspaceDir: params.workspaceDir,
env: params.env,
provider: params.auth.provider,
token: params.auth.token,
accountId: params.auth.accountId,
timeoutMs: params.timeoutMs,
fetchFn: params.fetchFn,
},
});
if (pluginSnapshot) {
return pluginSnapshot;
}
return await fetchProviderUsageSnapshotFallback({
auth: params.auth,
timeoutMs: params.timeoutMs,
fetchFn: params.fetchFn,
});
}
export async function loadProviderUsageSummary(
opts: UsageSummaryOptions = {},
): Promise<UsageSummary> {
const now = opts.now ?? Date.now();
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
const config = opts.config ?? getRuntimeConfig();
const env = opts.env ?? process.env;
const fetchFn = resolveFetch(opts.fetch);
if (!fetchFn) {
throw new Error("fetch is not available");
}
const auths = await resolveProviderAuths({
providers: opts.providers ?? usageProviders,
auth: opts.auth,
agentDir: opts.agentDir,
config,
env,
skipPluginAuthWithoutCredentialSource: opts.skipPluginAuthWithoutCredentialSource,
});
if (auths.length === 0) {
return { updatedAt: now, providers: [] };
}
const tasks = auths.map((auth) => {
const failureSnapshot = (error: string): ProviderUsageSnapshot => ({
provider: auth.provider,
displayName: PROVIDER_LABELS[auth.provider] ?? auth.provider,
windows: [],
error,
});
return withTimeout(
fetchProviderUsageSnapshot({
auth,
config,
env,
agentDir: opts.agentDir,
workspaceDir: opts.workspaceDir,
timeoutMs,
fetchFn,
}),
timeoutMs + 1000,
{
provider: auth.provider,
displayName: PROVIDER_LABELS[auth.provider],
windows: [],
error: "Timeout",
},
).catch((error: unknown) => {
const message = error instanceof Error ? error.message : String(error);
return failureSnapshot(message.trim() || "Fetch failed");
});
});
const snapshots = await Promise.all(tasks);
const providers = snapshots.filter((entry) => {
if (entry.windows.length > 0) {
return true;
}
if (entry.summary?.trim()) {
return true;
}
if (!entry.error) {
return true;
}
return !ignoredErrors.has(entry.error);
});
return { updatedAt: now, providers };
}