mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-11-05 23:37:25 +00:00
fix(ai): use OpenRouter-reported total cost (#10286)
Use authoritative OpenRouter billing for Chat Completions and Anthropic Messages while retaining catalog component estimates and fallback behavior. Fixes #9980
This commit is contained in:
parent
5884ee6816
commit
cae8d69e57
10 changed files with 324 additions and 4 deletions
|
|
@ -1351,7 +1351,7 @@
|
|||
let userMessages = 0, assistantMessages = 0, toolResults = 0;
|
||||
let customMessages = 0, compactions = 0, branchSummaries = 0, toolCalls = 0;
|
||||
const tokens = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
|
||||
const cost = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
|
||||
const cost = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 };
|
||||
const models = new Set();
|
||||
|
||||
for (const entry of entryList) {
|
||||
|
|
@ -1367,10 +1367,18 @@
|
|||
tokens.cacheRead += msg.usage.cacheRead || 0;
|
||||
tokens.cacheWrite += msg.usage.cacheWrite || 0;
|
||||
if (msg.usage.cost) {
|
||||
const componentTotal =
|
||||
(msg.usage.cost.input || 0) +
|
||||
(msg.usage.cost.output || 0) +
|
||||
(msg.usage.cost.cacheRead || 0) +
|
||||
(msg.usage.cost.cacheWrite || 0);
|
||||
cost.input += msg.usage.cost.input || 0;
|
||||
cost.output += msg.usage.cost.output || 0;
|
||||
cost.cacheRead += msg.usage.cost.cacheRead || 0;
|
||||
cost.cacheWrite += msg.usage.cost.cacheWrite || 0;
|
||||
// Provider-reported totals can intentionally differ from catalog-estimated components.
|
||||
// Fall back to the component sum for sessions written before cost.total was recorded.
|
||||
cost.total += typeof msg.usage.cost.total === 'number' ? msg.usage.cost.total : componentTotal;
|
||||
}
|
||||
}
|
||||
toolCalls += msg.content.filter(c => c.type === 'toolCall').length;
|
||||
|
|
@ -1391,7 +1399,7 @@
|
|||
const globalStats = computeStats(entries);
|
||||
|
||||
function renderHeader() {
|
||||
const totalCost = globalStats.cost.input + globalStats.cost.output + globalStats.cost.cacheRead + globalStats.cost.cacheWrite;
|
||||
const totalCost = globalStats.cost.total;
|
||||
|
||||
const tokenParts = [];
|
||||
if (globalStats.tokens.input) tokenParts.push(`↑${formatTokens(globalStats.tokens.input)}`);
|
||||
|
|
|
|||
52
packages/coding-agent/test/export-html-cost.test.ts
Normal file
52
packages/coding-agent/test/export-html-cost.test.ts
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
import { readFileSync } from "node:fs";
|
||||
import { runInNewContext } from "node:vm";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
type ExportStats = {
|
||||
cost: {
|
||||
input: number;
|
||||
output: number;
|
||||
cacheRead: number;
|
||||
cacheWrite: number;
|
||||
total: number;
|
||||
};
|
||||
};
|
||||
|
||||
describe("export HTML cost", () => {
|
||||
it("uses recorded totals while falling back to component sums for old sessions", () => {
|
||||
// Regression test for #9980: provider-reported totals may differ from catalog-estimated components.
|
||||
const templateJs = readFileSync(new URL("../src/core/export-html/template.js", import.meta.url), "utf-8");
|
||||
const functionStart = templateJs.indexOf("function computeStats(entryList)");
|
||||
const functionEnd = templateJs.indexOf("const globalStats = computeStats(entries);");
|
||||
expect(functionStart).toBeGreaterThanOrEqual(0);
|
||||
expect(functionEnd).toBeGreaterThan(functionStart);
|
||||
|
||||
const context: { result?: ExportStats } = {};
|
||||
runInNewContext(
|
||||
`${templateJs.slice(functionStart, functionEnd)}
|
||||
result = computeStats(${JSON.stringify([
|
||||
{
|
||||
type: "message",
|
||||
message: {
|
||||
role: "assistant",
|
||||
content: [],
|
||||
usage: { cost: { input: 0.1, output: 0.2, cacheRead: 0, cacheWrite: 0, total: 0.9 } },
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "message",
|
||||
message: {
|
||||
role: "assistant",
|
||||
content: [],
|
||||
usage: { cost: { input: 0.4, output: 0.1, cacheRead: 0, cacheWrite: 0 } },
|
||||
},
|
||||
},
|
||||
])});`,
|
||||
context,
|
||||
);
|
||||
|
||||
expect(context.result?.cost.input).toBeCloseTo(0.5);
|
||||
expect(context.result?.cost.output).toBeCloseTo(0.3);
|
||||
expect(context.result?.cost.total).toBeCloseTo(1.4);
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue