Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions apps/server/src/provider/Drivers/antigravityUsageReader.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import * as Layer from "effect/Layer";
import * as Path from "effect/Path";
import * as SqlClient from "effect/sql/SqlClient";

import { parseRateTable, priceUsage } from "../../usage/usagePricing.ts";

import { makeAntigravityUsageCache, readAntigravityUsage } from "./antigravityUsageReader.ts";

const openDatabase = Effect.fn("openDatabase")(function* (filename: string) {
Expand Down Expand Up @@ -162,6 +164,72 @@ describe("readAntigravityUsage", () => {
}).pipe(Effect.provide(NodeServices.layer)),
);

it.effect("prices Antigravity effort and routing variants at their base model", () =>
Effect.gen(function* () {
const path = yield* Path.Path;
const dir = yield* tempDir;
yield* seedDatabase(path.join(dir, "variants.db"), (db) =>
Effect.gen(function* () {
yield* createTables(db);
yield* db.run("INSERT INTO gen_metadata VALUES (?, ?)", [
0,
new Uint8Array(protoBytes(1, protoText(19, "Gemini 3.7 Flash Control"))),
]);
yield* db.run("INSERT INTO steps VALUES (?, ?)", [
0,
new Uint8Array(protoBytes(9, protoNumber(2, 10))),
]);
yield* db.run("INSERT INTO steps VALUES (?, ?)", [
1,
new Uint8Array(protoBytes(9, [...protoNumber(1, 1319), ...protoNumber(2, 11)])),
]);
yield* db.run("INSERT INTO steps VALUES (?, ?)", [
2,
new Uint8Array(protoBytes(9, [...protoNumber(1, 1132), ...protoNumber(2, 12)])),
]);
// A model-less step that only learns its model by merging with a named generation.
yield* db.run("INSERT INTO steps VALUES (?, ?)", [
5,
new Uint8Array(protoBytes(9, [...protoNumber(2, 100), ...protoText(11, "response")])),
]);
yield* db.run("INSERT INTO gen_metadata VALUES (?, ?)", [
3,
new Uint8Array(
protoBytes(1, [
...protoText(19, "Gemini 3.8 Flash High"),
...protoBytes(4, [...protoNumber(2, 100), ...protoText(11, "response")]),
]),
),
]);
}),
);
const result = yield* readAntigravityUsage(dir, 0);
assert.deepStrictEqual(result.errors, []);
assert.deepStrictEqual(
records(result).map((record) => [record.model, record.rateModel]),
[
["gemini-3.7-flash-control", "gemini-3.7-flash"],
["gemini-3.8-flash-medium", "gemini-3.8-flash"],
["gemini-3-flash-agent", "gemini-3-flash-preview"],
["gemini-3.8-flash-high", "gemini-3.8-flash"],
],
);
const table = parseRateTable({
"gemini-3.7-flash": { input_cost_per_token: 0.75e-6, output_cost_per_token: 3.75e-6 },
"gemini-3.8-flash": { input_cost_per_token: 0.75e-6, output_cost_per_token: 3.75e-6 },
"gemini-3-flash-preview": { input_cost_per_token: 0.5e-6, output_cost_per_token: 3e-6 },
});
const priced = records(result).map((record) => priceUsage(table, record));
assert.deepStrictEqual(
priced.map(({ costSource }) => costSource),
Array(4).fill("modelPriced"),
);
for (const [index, expected] of [7.5e-6, 8.25e-6, 6e-6, 75e-6].entries()) {
assert.closeTo(priced[index]!.costUsd, expected, 1e-12);
}
}).pipe(Effect.provide(NodeServices.layer)),
);

it.effect("prices ACP effort variants at the base model rate", () =>
Effect.gen(function* () {
const path = yield* Path.Path;
Expand Down
21 changes: 19 additions & 2 deletions apps/server/src/provider/Drivers/antigravityUsageReader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,28 @@ const MODEL_IDS: Record<number, string> = {
1018: "gemini-3-flash-preview",
1084: "gemini-3-flash-preview",
1047: "gemini-3-flash-preview",
1020: "gemini-3.5-flash-low",
1072: "gemini-3.6-flash-medium",
1073: "gemini-3.6-flash-low",
1132: "gemini-3-flash-agent",
1133: "gemini-3-flash-agent",
1187: "gemini-3.5-flash-extra-low",
1298: "gemini-3.7-flash-high",
1299: "gemini-3.7-flash-medium",
1301: "gemini-3.7-flash-tiered",
1318: "gemini-3.8-flash-high",
1319: "gemini-3.8-flash-medium",
1320: "gemini-3.8-flash-low",
1322: "gemini-3.8-flash-tiered",
};

// ACP slugs append the thinking level, which shares the base model's price.
// Effort and routing variants share the base model's price.
function withRateModel(record: UsageRecord): UsageRecord {
const rateModel = record.model.replace(/-(?:low|medium|high)$/, "");
const base = record.model.replace(
/-(?:extra-low|low|medium|high|tiered|control|thinking|agent)$/,
"",
);
const rateModel = base === "gemini-3-flash" ? "gemini-3-flash-preview" : base;
return rateModel === record.model ? record : { ...record, rateModel };
}

Expand Down
Loading