fix: align compaction cuts and native usage accounting
This commit is contained in:
@@ -87,8 +87,8 @@ export interface ChatSettings {
|
||||
* billed (CallUsage per agent step). Steps, not a sum: cost and context
|
||||
* folds happen at read time (core/prices.ts). */
|
||||
export interface ExtractionStageUsage {
|
||||
/** The model that ran this stage, provider-qualified ("provider:id" —
|
||||
* model slots wave, 2026-09-20; matches PRICES' uniform keys). */
|
||||
/** The model that ran this stage, provider-qualified ("provider:id")
|
||||
* for key-independent Pi catalog lookup at read time. */
|
||||
model: string;
|
||||
steps: CallUsage[];
|
||||
}
|
||||
@@ -118,7 +118,7 @@ export interface ExtractionRunUsage {
|
||||
* flattened to the fields we persist (undefined = provider didn't report;
|
||||
* renderers show absent, never zero). */
|
||||
export interface CallUsage {
|
||||
/** Total input (prompt) tokens, cached portion included. */
|
||||
/** Pi-normalized fresh input tokens; cache buckets are disjoint. */
|
||||
inputTokens: number | undefined;
|
||||
outputTokens: number | undefined;
|
||||
/** Cached input tokens READ (billed at the cache-read rate, if any). */
|
||||
@@ -126,6 +126,9 @@ export interface CallUsage {
|
||||
/** Input tokens written to the cache this call (OpenRouter bills writes
|
||||
* for some providers; undefined = not reported/not billed). */
|
||||
cacheWriteTokens: number | undefined;
|
||||
/** Subset of cacheWriteTokens written with one-hour retention. Missing
|
||||
* on older logs; new native captures explicitly record zero if absent. */
|
||||
cacheWrite1hTokens?: number;
|
||||
/** The reasoning portion of the output tokens. */
|
||||
reasoningTokens: number | undefined;
|
||||
}
|
||||
@@ -144,8 +147,8 @@ export interface TurnUsage {
|
||||
* entry to the transcript (the block this turn "belongs" to). */
|
||||
messageId: string;
|
||||
at: string;
|
||||
/** The model that ran the turn, provider-qualified ("provider:id" —
|
||||
* model slots wave, 2026-09-20; matches PRICES' uniform keys). */
|
||||
/** The model that ran the turn, provider-qualified ("provider:id")
|
||||
* for key-independent Pi catalog lookup at read time. */
|
||||
model: string;
|
||||
/** One entry per agent step (model call) in this turn. */
|
||||
steps: CallUsage[];
|
||||
|
||||
+21
-10
@@ -43,7 +43,7 @@ export interface JobRecord {
|
||||
* money-channel doctrine; cost renders "—", never $0). */
|
||||
export interface JobUsage {
|
||||
/** The model that ran the sub-agent, provider-qualified ("provider:id" —
|
||||
* model slots wave, 2026-09-20; matches PRICES' uniform keys). */
|
||||
* model slots wave, 2026-09-20; matches Pi catalog lookup keys). */
|
||||
model: string;
|
||||
/** Running totals summed over the run's model calls so far. */
|
||||
inputTokens: number | undefined;
|
||||
@@ -51,6 +51,10 @@ export interface JobUsage {
|
||||
cacheReadTokens: number | undefined;
|
||||
cacheWriteTokens: number | undefined;
|
||||
reasoningTokens: number | undefined;
|
||||
cacheWrite1hTokens?: number;
|
||||
/** Individual calls for current-catalog per-call tier pricing. Absent on
|
||||
* older jobs; never backfill them from cumulative totals. */
|
||||
calls?: CallUsage[];
|
||||
/** How many model calls the run has made (each bills separately). */
|
||||
steps: number;
|
||||
/** The MOST RECENT model call's accounting (2026-09 dock contract): the
|
||||
@@ -67,7 +71,7 @@ export interface JobUsage {
|
||||
* object with these fields folds, including the real thing and test fakes). */
|
||||
export interface UsageStepLike {
|
||||
inputTokens?: number | undefined;
|
||||
inputTokenDetails?: { cacheReadTokens?: number; cacheWriteTokens?: number } | undefined;
|
||||
inputTokenDetails?: { cacheReadTokens?: number; cacheWriteTokens?: number; cacheWrite1hTokens?: number } | undefined;
|
||||
outputTokens?: number | undefined;
|
||||
outputTokenDetails?: { reasoningTokens?: number } | undefined;
|
||||
}
|
||||
@@ -80,12 +84,13 @@ export function piUsageToStep(usage: {
|
||||
output: number;
|
||||
cacheRead: number;
|
||||
cacheWrite: number;
|
||||
cacheWrite1h?: number;
|
||||
reasoning?: number;
|
||||
}): UsageStepLike {
|
||||
return {
|
||||
inputTokens: usage.input,
|
||||
outputTokens: usage.output,
|
||||
inputTokenDetails: { cacheReadTokens: usage.cacheRead, cacheWriteTokens: usage.cacheWrite },
|
||||
inputTokenDetails: { cacheReadTokens: usage.cacheRead, cacheWriteTokens: usage.cacheWrite, cacheWrite1hTokens: usage.cacheWrite1h ?? 0 },
|
||||
outputTokenDetails: usage.reasoning !== undefined ? { reasoningTokens: usage.reasoning } : undefined,
|
||||
};
|
||||
}
|
||||
@@ -101,6 +106,14 @@ export function accumulateJobUsage(
|
||||
): JobUsage {
|
||||
const add = (a: number | undefined, b: number | undefined): number | undefined =>
|
||||
a === undefined && b === undefined ? undefined : (a ?? 0) + (b ?? 0);
|
||||
const call: CallUsage = {
|
||||
inputTokens: step.inputTokens,
|
||||
outputTokens: step.outputTokens,
|
||||
cacheReadTokens: step.inputTokenDetails?.cacheReadTokens,
|
||||
cacheWriteTokens: step.inputTokenDetails?.cacheWriteTokens,
|
||||
cacheWrite1hTokens: step.inputTokenDetails?.cacheWrite1hTokens,
|
||||
reasoningTokens: step.outputTokenDetails?.reasoningTokens,
|
||||
};
|
||||
return {
|
||||
model,
|
||||
inputTokens: add(total?.inputTokens, step.inputTokens),
|
||||
@@ -108,14 +121,12 @@ export function accumulateJobUsage(
|
||||
cacheReadTokens: add(total?.cacheReadTokens, step.inputTokenDetails?.cacheReadTokens),
|
||||
cacheWriteTokens: add(total?.cacheWriteTokens, step.inputTokenDetails?.cacheWriteTokens),
|
||||
reasoningTokens: add(total?.reasoningTokens, step.outputTokenDetails?.reasoningTokens),
|
||||
cacheWrite1hTokens: ((total?.cacheWriteTokens ?? 0) > 0 && total?.cacheWrite1hTokens === undefined) ||
|
||||
((call.cacheWriteTokens ?? 0) > 0 && call.cacheWrite1hTokens === undefined)
|
||||
? undefined : add(total?.cacheWrite1hTokens, call.cacheWrite1hTokens),
|
||||
steps: (total?.steps ?? 0) + 1,
|
||||
lastStep: {
|
||||
inputTokens: step.inputTokens,
|
||||
outputTokens: step.outputTokens,
|
||||
cacheReadTokens: step.inputTokenDetails?.cacheReadTokens,
|
||||
cacheWriteTokens: step.inputTokenDetails?.cacheWriteTokens,
|
||||
reasoningTokens: step.outputTokenDetails?.reasoningTokens,
|
||||
},
|
||||
...(!total || total.calls ? { calls: [...(total?.calls ?? []), call] } : {}),
|
||||
lastStep: call,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import type { BackgroundModelRole, ModelSlotName } from "../settings.ts";
|
||||
|
||||
/** One picker row: everything the UI shows about a model, projected from
|
||||
* pi-ai's catalog. Costs are USD per 1M tokens (the same catalog, the same
|
||||
* units core/prices.ts's generated table carries). */
|
||||
* units as Pi's server-side pricing calculator). */
|
||||
export interface ModelCatalogEntry {
|
||||
id: string;
|
||||
name: string;
|
||||
|
||||
Reference in New Issue
Block a user