| import type { NormalizedUsage } from "../agents/usage.js"; |
| import type { OpenClawConfig } from "../config/config.js"; |
|
|
| export type ModelCostConfig = { |
| input: number; |
| output: number; |
| cacheRead: number; |
| cacheWrite: number; |
| }; |
|
|
| export type UsageTotals = { |
| input?: number; |
| output?: number; |
| cacheRead?: number; |
| cacheWrite?: number; |
| total?: number; |
| }; |
|
|
| export function formatTokenCount(value?: number): string { |
| if (value === undefined || !Number.isFinite(value)) { |
| return "0"; |
| } |
| const safe = Math.max(0, value); |
| if (safe >= 1_000_000) { |
| return `${(safe / 1_000_000).toFixed(1)}m`; |
| } |
| if (safe >= 1_000) { |
| const precision = safe >= 10_000 ? 0 : 1; |
| const formattedThousands = (safe / 1_000).toFixed(precision); |
| if (Number(formattedThousands) >= 1_000) { |
| return `${(safe / 1_000_000).toFixed(1)}m`; |
| } |
| return `${formattedThousands}k`; |
| } |
| return String(Math.round(safe)); |
| } |
|
|
| export function formatUsd(value?: number): string | undefined { |
| if (value === undefined || !Number.isFinite(value)) { |
| return undefined; |
| } |
| if (value >= 1) { |
| return `$${value.toFixed(2)}`; |
| } |
| if (value >= 0.01) { |
| return `$${value.toFixed(2)}`; |
| } |
| return `$${value.toFixed(4)}`; |
| } |
|
|
| export function resolveModelCostConfig(params: { |
| provider?: string; |
| model?: string; |
| config?: OpenClawConfig; |
| }): ModelCostConfig | undefined { |
| const provider = params.provider?.trim(); |
| const model = params.model?.trim(); |
| if (!provider || !model) { |
| return undefined; |
| } |
| const providers = params.config?.models?.providers ?? {}; |
| const entry = providers[provider]?.models?.find((item) => item.id === model); |
| return entry?.cost; |
| } |
|
|
| const toNumber = (value: number | undefined): number => |
| typeof value === "number" && Number.isFinite(value) ? value : 0; |
|
|
| export function estimateUsageCost(params: { |
| usage?: NormalizedUsage | UsageTotals | null; |
| cost?: ModelCostConfig; |
| }): number | undefined { |
| const usage = params.usage; |
| const cost = params.cost; |
| if (!usage || !cost) { |
| return undefined; |
| } |
| const input = toNumber(usage.input); |
| const output = toNumber(usage.output); |
| const cacheRead = toNumber(usage.cacheRead); |
| const cacheWrite = toNumber(usage.cacheWrite); |
| const total = |
| input * cost.input + |
| output * cost.output + |
| cacheRead * cost.cacheRead + |
| cacheWrite * cost.cacheWrite; |
| if (!Number.isFinite(total)) { |
| return undefined; |
| } |
| return total / 1_000_000; |
| } |
|
|