fix: stop guessing session model costs
This commit is contained in:
parent
6bc1f779df
commit
de22f822e0
@ -83,10 +83,23 @@ describe("models-config", () => {
|
||||
const modelPath = path.join(resolveOpenClawAgentDir(), "models.json");
|
||||
const raw = await fs.readFile(modelPath, "utf8");
|
||||
const parsed = JSON.parse(raw) as {
|
||||
providers: Record<string, { baseUrl?: string }>;
|
||||
providers: Record<
|
||||
string,
|
||||
{
|
||||
baseUrl?: string;
|
||||
models?: Array<{
|
||||
id?: string;
|
||||
cost?: { input?: number; output?: number; cacheRead?: number; cacheWrite?: number };
|
||||
}>;
|
||||
}
|
||||
>;
|
||||
};
|
||||
|
||||
expect(parsed.providers["custom-proxy"]?.baseUrl).toBe("http://localhost:4000/v1");
|
||||
expect(parsed.providers["custom-proxy"]?.models?.[0]).toMatchObject({
|
||||
id: "llama-3.1-8b",
|
||||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@ -123,6 +123,7 @@ describe("sessions tools", () => {
|
||||
status: "running",
|
||||
startedAt: 100,
|
||||
runtimeMs: 42,
|
||||
estimatedCostUsd: 0.0042,
|
||||
childSessions: ["agent:main:subagent:worker"],
|
||||
},
|
||||
{
|
||||
@ -164,6 +165,7 @@ describe("sessions tools", () => {
|
||||
status?: string;
|
||||
startedAt?: number;
|
||||
runtimeMs?: number;
|
||||
estimatedCostUsd?: number;
|
||||
childSessions?: string[];
|
||||
messages?: Array<{ role?: string }>;
|
||||
}>;
|
||||
@ -178,6 +180,7 @@ describe("sessions tools", () => {
|
||||
expect(group?.status).toBe("running");
|
||||
expect(group?.startedAt).toBe(100);
|
||||
expect(group?.runtimeMs).toBe(42);
|
||||
expect(group?.estimatedCostUsd).toBe(0.0042);
|
||||
expect(group?.childSessions).toEqual(["agent:main:subagent:worker"]);
|
||||
|
||||
const cronOnly = await tool.execute("call2", { kinds: ["cron"] });
|
||||
|
||||
@ -58,6 +58,7 @@ export type SessionListRow = {
|
||||
model?: string;
|
||||
contextTokens?: number | null;
|
||||
totalTokens?: number | null;
|
||||
estimatedCostUsd?: number;
|
||||
status?: SessionRunStatus;
|
||||
startedAt?: number;
|
||||
endedAt?: number;
|
||||
|
||||
@ -203,6 +203,8 @@ export function createSessionsListTool(opts?: {
|
||||
model: typeof entry.model === "string" ? entry.model : undefined,
|
||||
contextTokens: typeof entry.contextTokens === "number" ? entry.contextTokens : undefined,
|
||||
totalTokens: typeof entry.totalTokens === "number" ? entry.totalTokens : undefined,
|
||||
estimatedCostUsd:
|
||||
typeof entry.estimatedCostUsd === "number" ? entry.estimatedCostUsd : undefined,
|
||||
status: typeof entry.status === "string" ? entry.status : undefined,
|
||||
startedAt: typeof entry.startedAt === "number" ? entry.startedAt : undefined,
|
||||
endedAt: typeof entry.endedAt === "number" ? entry.endedAt : undefined,
|
||||
|
||||
@ -60,33 +60,23 @@ describe("usage-format", () => {
|
||||
expect(total).toBeCloseTo(0.003);
|
||||
});
|
||||
|
||||
it("falls back to built in pricing for common models", () => {
|
||||
it("returns undefined when model pricing is not configured", () => {
|
||||
expect(
|
||||
resolveModelCostConfig({
|
||||
provider: "anthropic",
|
||||
model: "claude-sonnet-4-6",
|
||||
}),
|
||||
).toEqual({
|
||||
input: 3,
|
||||
output: 15,
|
||||
cacheRead: 0.3,
|
||||
cacheWrite: 3.75,
|
||||
});
|
||||
).toBeUndefined();
|
||||
|
||||
expect(
|
||||
resolveModelCostConfig({
|
||||
provider: "openai-codex",
|
||||
model: "gpt-5.4",
|
||||
}),
|
||||
).toEqual({
|
||||
input: 2,
|
||||
output: 8,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
});
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
it("prefers configured pricing over built in defaults", () => {
|
||||
it("uses configured pricing when present", () => {
|
||||
const config = {
|
||||
models: {
|
||||
providers: {
|
||||
|
||||
@ -48,45 +48,6 @@ export function formatUsd(value?: number): string | undefined {
|
||||
return `$${value.toFixed(4)}`;
|
||||
}
|
||||
|
||||
const BUILTIN_MODEL_COSTS: Record<string, ModelCostConfig> = {
|
||||
"anthropic/claude-opus-4-6": {
|
||||
input: 15,
|
||||
output: 75,
|
||||
cacheRead: 1.5,
|
||||
cacheWrite: 18.75,
|
||||
},
|
||||
"anthropic/claude-sonnet-4-6": {
|
||||
input: 3,
|
||||
output: 15,
|
||||
cacheRead: 0.3,
|
||||
cacheWrite: 3.75,
|
||||
},
|
||||
"anthropic/claude-haiku-4-5": {
|
||||
input: 0.8,
|
||||
output: 4,
|
||||
cacheRead: 0.08,
|
||||
cacheWrite: 1,
|
||||
},
|
||||
"openai-codex/gpt-5.4": {
|
||||
input: 2,
|
||||
output: 8,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
"openai-codex/gpt-5.3-codex": {
|
||||
input: 1,
|
||||
output: 4,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
"openai-codex/gpt-5.3-codex-spark": {
|
||||
input: 0.5,
|
||||
output: 2,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
};
|
||||
|
||||
export function resolveModelCostConfig(params: {
|
||||
provider?: string;
|
||||
model?: string;
|
||||
@ -99,10 +60,7 @@ export function resolveModelCostConfig(params: {
|
||||
}
|
||||
const providers = params.config?.models?.providers ?? {};
|
||||
const entry = providers[provider]?.models?.find((item) => item.id === model);
|
||||
if (entry?.cost) {
|
||||
return entry.cost;
|
||||
}
|
||||
return BUILTIN_MODEL_COSTS[`${provider.toLowerCase()}/${model.toLowerCase()}`];
|
||||
return entry?.cost;
|
||||
}
|
||||
|
||||
const toNumber = (value: number | undefined): number =>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user