- Added a Usage.orchestration sidecar for provider-side service tokens so Responses/Codex totals and costs stay accurate without inflating visible prompt input/cache buckets. - Updated Codex/WebSocket usage, session/status aggregates, and usage reporting to preserve orchestration-aware totals. - Added regressions for OpenAI Responses accounting, Codex WebSocket terminal usage, cost calculation, and session aggregation. Fixes #4469
125 lines
3.1 KiB
TypeScript
125 lines
3.1 KiB
TypeScript
import { describe, expect, it } from "bun:test";
|
|
import type { Usage } from "@oh-my-pi/pi-ai/types";
|
|
import { calculateCost, getBundledModel } from "@oh-my-pi/pi-catalog/models";
|
|
|
|
describe("calculateCost", () => {
|
|
it("keeps token-based calculation for GitHub Copilot models", () => {
|
|
const model = {
|
|
...getBundledModel("github-copilot", "gpt-4o"),
|
|
cost: {
|
|
input: 1000,
|
|
output: 2000,
|
|
cacheRead: 500,
|
|
cacheWrite: 800,
|
|
},
|
|
};
|
|
const usage: Usage = {
|
|
input: 1000,
|
|
output: 500,
|
|
cacheRead: 200,
|
|
cacheWrite: 100,
|
|
totalTokens: 1800,
|
|
cost: {
|
|
input: 123,
|
|
output: 456,
|
|
cacheRead: 789,
|
|
cacheWrite: 321,
|
|
total: 1689,
|
|
},
|
|
};
|
|
|
|
calculateCost(model, usage);
|
|
|
|
expect(usage.cost.input).toBeCloseTo(1, 8);
|
|
expect(usage.cost.output).toBeCloseTo(1, 8);
|
|
expect(usage.cost.cacheRead).toBeCloseTo(0.1, 8);
|
|
expect(usage.cost.cacheWrite).toBeCloseTo(0.08, 8);
|
|
expect(usage.cost.total).toBeCloseTo(2.18, 8);
|
|
});
|
|
|
|
it("keeps token-based calculation for non-Copilot providers", () => {
|
|
const model = {
|
|
...getBundledModel("openai", "gpt-4o-mini"),
|
|
cost: {
|
|
input: 1000,
|
|
output: 2000,
|
|
cacheRead: 500,
|
|
cacheWrite: 800,
|
|
},
|
|
};
|
|
const usage: Usage = {
|
|
input: 1000,
|
|
output: 500,
|
|
cacheRead: 200,
|
|
cacheWrite: 100,
|
|
totalTokens: 1800,
|
|
cost: {
|
|
input: 0,
|
|
output: 0,
|
|
cacheRead: 0,
|
|
cacheWrite: 0,
|
|
total: 0,
|
|
},
|
|
};
|
|
|
|
calculateCost(model, usage);
|
|
|
|
expect(usage.cost.input).toBeCloseTo(1, 8);
|
|
expect(usage.cost.output).toBeCloseTo(1, 8);
|
|
expect(usage.cost.cacheRead).toBeCloseTo(0.1, 8);
|
|
expect(usage.cost.cacheWrite).toBeCloseTo(0.08, 8);
|
|
expect(usage.cost.total).toBeCloseTo(2.18, 8);
|
|
});
|
|
|
|
it("prices provider orchestration tokens without changing visible usage buckets", () => {
|
|
const model = {
|
|
...getBundledModel("openai", "gpt-4o-mini"),
|
|
cost: {
|
|
input: 1000,
|
|
output: 2000,
|
|
cacheRead: 500,
|
|
cacheWrite: 800,
|
|
},
|
|
};
|
|
const usage: Usage = {
|
|
input: 100,
|
|
output: 20,
|
|
cacheRead: 50,
|
|
cacheWrite: 10,
|
|
totalTokens: 250,
|
|
orchestration: { input: 25, output: 40, cacheRead: 5 },
|
|
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
};
|
|
|
|
calculateCost(model, usage);
|
|
|
|
expect(usage.input).toBe(100);
|
|
expect(usage.output).toBe(20);
|
|
expect(usage.cacheRead).toBe(50);
|
|
expect(usage.cost.input).toBeCloseTo(0.125, 8);
|
|
expect(usage.cost.output).toBeCloseTo(0.12, 8);
|
|
expect(usage.cost.cacheRead).toBeCloseTo(0.0275, 8);
|
|
expect(usage.cost.cacheWrite).toBeCloseTo(0.008, 8);
|
|
expect(usage.cost.total).toBeCloseTo(0.2805, 8);
|
|
});
|
|
|
|
it("prices OpenAI Codex GPT models from the matching OpenAI catalog entry", () => {
|
|
const openAIModel = getBundledModel("openai", "gpt-5.4");
|
|
const codexModel = getBundledModel("openai-codex", "gpt-5.4");
|
|
const usage: Usage = {
|
|
input: 1000,
|
|
output: 500,
|
|
cacheRead: 200,
|
|
cacheWrite: 0,
|
|
totalTokens: 1700,
|
|
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
};
|
|
|
|
expect(codexModel.cost).toEqual(openAIModel.cost);
|
|
|
|
calculateCost(codexModel, usage);
|
|
|
|
expect(usage.cost.total).toBeCloseTo(0.01005, 8);
|
|
});
|
|
});
|