@knpkv/agent-usage 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +111 -0
- package/dist/client/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
- package/dist/client/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
- package/dist/client/assets/index-CUuLXODk.css +1 -0
- package/dist/client/assets/index-DB-VXWqy.js +55 -0
- package/dist/client/index.html +15 -0
- package/dist/core/Attribution.d.ts +59 -0
- package/dist/core/Attribution.js +108 -0
- package/dist/core/Attribution.js.map +1 -0
- package/dist/core/Balances.d.ts +27 -0
- package/dist/core/Balances.js +63 -0
- package/dist/core/Balances.js.map +1 -0
- package/dist/core/ClaudeLimits.d.ts +58 -0
- package/dist/core/ClaudeLimits.js +125 -0
- package/dist/core/ClaudeLimits.js.map +1 -0
- package/dist/core/ClaudeLimitsLive.d.ts +18 -0
- package/dist/core/ClaudeLimitsLive.js +36 -0
- package/dist/core/ClaudeLimitsLive.js.map +1 -0
- package/dist/core/ClaudeReader.d.ts +27 -0
- package/dist/core/ClaudeReader.js +154 -0
- package/dist/core/ClaudeReader.js.map +1 -0
- package/dist/core/CodexReader.d.ts +91 -0
- package/dist/core/CodexReader.js +365 -0
- package/dist/core/CodexReader.js.map +1 -0
- package/dist/core/Database.d.ts +4 -0
- package/dist/core/Database.js +65 -0
- package/dist/core/Database.js.map +1 -0
- package/dist/core/Ingest.d.ts +55 -0
- package/dist/core/Ingest.js +267 -0
- package/dist/core/Ingest.js.map +1 -0
- package/dist/core/Model.d.ts +160 -0
- package/dist/core/Model.js +125 -0
- package/dist/core/Model.js.map +1 -0
- package/dist/core/Pricing.d.ts +42 -0
- package/dist/core/Pricing.js +126 -0
- package/dist/core/Pricing.js.map +1 -0
- package/dist/core/Readers.d.ts +42 -0
- package/dist/core/Readers.js +18 -0
- package/dist/core/Readers.js.map +1 -0
- package/dist/core/Report.d.ts +53 -0
- package/dist/core/Report.js +270 -0
- package/dist/core/Report.js.map +1 -0
- package/dist/core/Store.d.ts +94 -0
- package/dist/core/Store.js +376 -0
- package/dist/core/Store.js.map +1 -0
- package/dist/core/Tickets.d.ts +42 -0
- package/dist/core/Tickets.js +125 -0
- package/dist/core/Tickets.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/main.d.ts +2 -0
- package/dist/main.js +65 -0
- package/dist/main.js.map +1 -0
- package/dist/server/Api.d.ts +304 -0
- package/dist/server/Api.js +20 -0
- package/dist/server/Api.js.map +1 -0
- package/dist/server/Config.d.ts +27 -0
- package/dist/server/Config.js +49 -0
- package/dist/server/Config.js.map +1 -0
- package/dist/server/Handlers.d.ts +5 -0
- package/dist/server/Handlers.js +48 -0
- package/dist/server/Handlers.js.map +1 -0
- package/dist/server/HttpApplication.d.ts +7 -0
- package/dist/server/HttpApplication.js +70 -0
- package/dist/server/HttpApplication.js.map +1 -0
- package/dist/server/OwnerSession.d.ts +60 -0
- package/dist/server/OwnerSession.js +206 -0
- package/dist/server/OwnerSession.js.map +1 -0
- package/dist/server/Runtime.d.ts +57 -0
- package/dist/server/Runtime.js +85 -0
- package/dist/server/Runtime.js.map +1 -0
- package/dist/server/Server.d.ts +15 -0
- package/dist/server/Server.js +39 -0
- package/dist/server/Server.js.map +1 -0
- package/dist/shared/contracts.d.ts +309 -0
- package/dist/shared/contracts.js +105 -0
- package/dist/shared/contracts.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +68 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The facts a store records: Usage Events, Limit Snapshots and Balance Readings.
|
|
3
|
+
*
|
|
4
|
+
* **Mental model**
|
|
5
|
+
*
|
|
6
|
+
* - **Facts, not conclusions.** A Usage Event carries token counts and Attribution Inputs; its
|
|
7
|
+
* Booking and API-Equivalent Cost are derived when read (ADR 0002).
|
|
8
|
+
* - **Unknown is a value.** A limit or balance that could not be read is stored as `Unknown` with a
|
|
9
|
+
* reason, so a gap on a graph is a gap and never a zero.
|
|
10
|
+
*
|
|
11
|
+
* @module
|
|
12
|
+
*/
|
|
13
|
+
import { Schema } from "effect";
|
|
14
|
+
/** The closed set of coding-agent runtimes whose usage is recorded. */
|
|
15
|
+
export const Agent = Schema.Literals(["claude", "codex"]);
|
|
16
|
+
/** A token count: a whole number, never negative. */
|
|
17
|
+
export const Count = Schema.Int.check(Schema.isGreaterThanOrEqualTo(0));
|
|
18
|
+
/**
|
|
19
|
+
* One request's token counts, split the way pricing needs them.
|
|
20
|
+
*
|
|
21
|
+
* `input` is uncached input only, for both agents: Codex reports cache hits inside its input count
|
|
22
|
+
* and the readers subtract them, so the columns mean the same thing whichever agent wrote them.
|
|
23
|
+
* Codex reports one cache-write count; it is stored as `cacheWrite5m`. `output` excludes
|
|
24
|
+
* `reasoning` for both agents (Codex counts reasoning inside its output figure, Claude reports no
|
|
25
|
+
* split), so the six fields add up to every token the request moved.
|
|
26
|
+
*/
|
|
27
|
+
export const Tokens = Schema.Struct({
|
|
28
|
+
input: Count,
|
|
29
|
+
output: Count,
|
|
30
|
+
reasoning: Count,
|
|
31
|
+
cacheRead: Count,
|
|
32
|
+
cacheWrite5m: Count,
|
|
33
|
+
cacheWrite1h: Count
|
|
34
|
+
});
|
|
35
|
+
/** Every token a request moved, the sum the "tokens" measure plots. */
|
|
36
|
+
export const totalTokens = (tokens) => tokens.input + tokens.output + tokens.reasoning + tokens.cacheRead + tokens.cacheWrite5m + tokens.cacheWrite1h;
|
|
37
|
+
/** The facts captured with a Usage Event that decide its Booking. */
|
|
38
|
+
export const AttributionInputs = Schema.Struct({
|
|
39
|
+
cwd: Schema.String,
|
|
40
|
+
branch: Schema.String,
|
|
41
|
+
activeTicket: Schema.NullOr(Schema.String)
|
|
42
|
+
});
|
|
43
|
+
/**
|
|
44
|
+
* One model request's token counts on one Machine. `dedupeKey` is unique per Agent: Claude's
|
|
45
|
+
* message id + request id, Codex's session id + the line's byte offset in its rollout.
|
|
46
|
+
*/
|
|
47
|
+
export const UsageEvent = Schema.Struct({
|
|
48
|
+
agent: Agent,
|
|
49
|
+
dedupeKey: Schema.NonEmptyString,
|
|
50
|
+
machine: Schema.NonEmptyString,
|
|
51
|
+
sessionId: Schema.NonEmptyString,
|
|
52
|
+
occurredAt: Schema.Int,
|
|
53
|
+
model: Schema.NonEmptyString,
|
|
54
|
+
/** Claude's fast mode: the same model billed at premium rates. */
|
|
55
|
+
fast: Schema.Boolean,
|
|
56
|
+
tokens: Tokens,
|
|
57
|
+
attribution: AttributionInputs
|
|
58
|
+
});
|
|
59
|
+
/** Why a limit or balance could not be read. */
|
|
60
|
+
export const UnknownReason = Schema.Literals([
|
|
61
|
+
"NoAuth",
|
|
62
|
+
"AuthExpired",
|
|
63
|
+
"NotSupported",
|
|
64
|
+
"Fetch",
|
|
65
|
+
"Parse",
|
|
66
|
+
"NoData"
|
|
67
|
+
]);
|
|
68
|
+
/** Where a Limit Snapshot or Balance Reading came from. */
|
|
69
|
+
export const ObservationSource = Schema.Literals(["claude-oauth-usage", "codex-rollout"]);
|
|
70
|
+
/** A window's length in minutes, or Unknown for a provider key whose window nobody published. */
|
|
71
|
+
export const WindowMinutes = Schema.NullOr(Schema.Int.check(Schema.isGreaterThan(0)));
|
|
72
|
+
export const LimitReading = Schema.Union([
|
|
73
|
+
Schema.TaggedStruct("Known", {
|
|
74
|
+
usedPercent: Schema.Finite,
|
|
75
|
+
/** Epoch milliseconds; null when the provider gave no reset time. */
|
|
76
|
+
resetsAt: Schema.NullOr(Schema.Int)
|
|
77
|
+
}),
|
|
78
|
+
Schema.TaggedStruct("Unknown", { reason: UnknownReason })
|
|
79
|
+
]);
|
|
80
|
+
/**
|
|
81
|
+
* One observation of a Limit Window. `label` is the provider's own key (`five_hour`, `seven_day`,
|
|
82
|
+
* Codex's `primary`/`secondary`); an Unknown reading for a whole source uses label `*`.
|
|
83
|
+
*/
|
|
84
|
+
export const LimitSnapshot = Schema.Struct({
|
|
85
|
+
agent: Agent,
|
|
86
|
+
machine: Schema.NonEmptyString,
|
|
87
|
+
source: ObservationSource,
|
|
88
|
+
label: Schema.NonEmptyString,
|
|
89
|
+
windowMinutes: WindowMinutes,
|
|
90
|
+
observedAt: Schema.Int,
|
|
91
|
+
reading: LimitReading
|
|
92
|
+
});
|
|
93
|
+
export const Balance = Schema.Union([
|
|
94
|
+
/** Claude extra usage left this billing period, in minor units of `currency`. */
|
|
95
|
+
Schema.TaggedStruct("Amount", {
|
|
96
|
+
leftMinor: Schema.Finite,
|
|
97
|
+
limitMinor: Schema.Finite,
|
|
98
|
+
decimals: Schema.Int,
|
|
99
|
+
currency: Schema.String
|
|
100
|
+
}),
|
|
101
|
+
/** Codex credits, in Codex's own unit. */
|
|
102
|
+
Schema.TaggedStruct("Credits", { credits: Schema.Finite }),
|
|
103
|
+
Schema.TaggedStruct("Unlimited", {}),
|
|
104
|
+
Schema.TaggedStruct("Exhausted", {}),
|
|
105
|
+
/** Extra usage is switched off, so there is nothing to spend. */
|
|
106
|
+
Schema.TaggedStruct("Disabled", {})
|
|
107
|
+
]);
|
|
108
|
+
export const BalanceValue = Schema.Union([
|
|
109
|
+
Schema.TaggedStruct("Known", { balance: Balance }),
|
|
110
|
+
Schema.TaggedStruct("Unknown", { reason: UnknownReason })
|
|
111
|
+
]);
|
|
112
|
+
/** Which balance a reading is of. */
|
|
113
|
+
export const BalanceKind = Schema.Literals(["claude-extra-usage", "codex-credits"]);
|
|
114
|
+
export const BalanceReading = Schema.Struct({
|
|
115
|
+
kind: BalanceKind,
|
|
116
|
+
machine: Schema.NonEmptyString,
|
|
117
|
+
observedAt: Schema.Int,
|
|
118
|
+
value: BalanceValue
|
|
119
|
+
});
|
|
120
|
+
/** A ticket's Jira title, or why there is none. */
|
|
121
|
+
export const TicketTitleValue = Schema.Union([
|
|
122
|
+
Schema.TaggedStruct("Known", { summary: Schema.String }),
|
|
123
|
+
Schema.TaggedStruct("Unknown", { reason: Schema.Literals(["NotFound", "NotLookedUp"]) })
|
|
124
|
+
]);
|
|
125
|
+
//# sourceMappingURL=Model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Model.js","sourceRoot":"","sources":["../../src/core/Model.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API-Equivalent Cost: what a Usage Event's tokens would cost at today's list price.
|
|
3
|
+
*
|
|
4
|
+
* **Mental model**
|
|
5
|
+
*
|
|
6
|
+
* - **Priced when read** (ADR 0002). Nothing here is persisted, so updating a rate or adding a model
|
|
7
|
+
* reprices every event already stored, past ones included. The UI says so.
|
|
8
|
+
* - **Unknown is not free.** A model missing from the table, or a cache rate the table lacks for
|
|
9
|
+
* tokens that were actually cached, yields `Option.none()`.
|
|
10
|
+
* - **No network.** The tables are a snapshot: Claude's from Anthropic's list prices, Codex's from
|
|
11
|
+
* the models.dev catalog. Updating them is a release.
|
|
12
|
+
*
|
|
13
|
+
* @module
|
|
14
|
+
*/
|
|
15
|
+
import { Option } from "effect";
|
|
16
|
+
import type { Agent, Tokens } from "./Model.js";
|
|
17
|
+
/** A Codex request whose prompt exceeds this many tokens is billed at its model's long-context tier. */
|
|
18
|
+
export declare const LONG_PROMPT_TOKENS = 272000;
|
|
19
|
+
/** Every token of a request's prompt: uncached input, cache reads and cache writes. */
|
|
20
|
+
export declare const promptTokens: (tokens: Tokens) => number;
|
|
21
|
+
/** True when one request's prompt falls in the long-context tier. */
|
|
22
|
+
export declare const isLongPrompt: (tokens: Tokens) => boolean;
|
|
23
|
+
/** `claude-opus-5[1m]` and `claude-opus-5-20260101` both price as `claude-opus-5`. */
|
|
24
|
+
export declare const normalizeClaudeModel: (model: string) => string;
|
|
25
|
+
/** Requests that price alike: one agent, model and speed, all Long Prompts or none. */
|
|
26
|
+
export interface PricedGroup {
|
|
27
|
+
readonly agent: Agent;
|
|
28
|
+
readonly model: string;
|
|
29
|
+
readonly fast: boolean;
|
|
30
|
+
readonly longPrompt: boolean;
|
|
31
|
+
/** The group's summed tokens. Pricing is linear within a group, so a sum prices like its parts. */
|
|
32
|
+
readonly tokens: Tokens;
|
|
33
|
+
}
|
|
34
|
+
/** A group's cost at current list price, or none when its model or a needed rate is unpriced. */
|
|
35
|
+
export declare const groupCost: (group: PricedGroup) => Option.Option<number>;
|
|
36
|
+
/** One request's cost at current list price, or none when its model or a needed rate is unpriced. */
|
|
37
|
+
export declare const apiEquivalentCost: (request: {
|
|
38
|
+
readonly agent: Agent;
|
|
39
|
+
readonly model: string;
|
|
40
|
+
readonly fast: boolean;
|
|
41
|
+
readonly tokens: Tokens;
|
|
42
|
+
}) => Option.Option<number>;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API-Equivalent Cost: what a Usage Event's tokens would cost at today's list price.
|
|
3
|
+
*
|
|
4
|
+
* **Mental model**
|
|
5
|
+
*
|
|
6
|
+
* - **Priced when read** (ADR 0002). Nothing here is persisted, so updating a rate or adding a model
|
|
7
|
+
* reprices every event already stored, past ones included. The UI says so.
|
|
8
|
+
* - **Unknown is not free.** A model missing from the table, or a cache rate the table lacks for
|
|
9
|
+
* tokens that were actually cached, yields `Option.none()`.
|
|
10
|
+
* - **No network.** The tables are a snapshot: Claude's from Anthropic's list prices, Codex's from
|
|
11
|
+
* the models.dev catalog. Updating them is a release.
|
|
12
|
+
*
|
|
13
|
+
* @module
|
|
14
|
+
*/
|
|
15
|
+
import { Option } from "effect";
|
|
16
|
+
const PER_MTOK = 1_000_000;
|
|
17
|
+
// Cache writes cost more than fresh input; reads cost a tenth of it.
|
|
18
|
+
const CLAUDE_CACHE_WRITE_5M = 1.25;
|
|
19
|
+
const CLAUDE_CACHE_WRITE_1H = 2;
|
|
20
|
+
const CLAUDE_CACHE_READ = 0.1;
|
|
21
|
+
/** USD per million tokens, keyed by normalized model id. */
|
|
22
|
+
const CLAUDE_STANDARD = new Map([
|
|
23
|
+
["claude-fable-5-1", { input: 10, output: 50 }],
|
|
24
|
+
["claude-fable-5", { input: 10, output: 50 }],
|
|
25
|
+
["claude-mythos-5-1", { input: 10, output: 50 }],
|
|
26
|
+
["claude-mythos-5", { input: 10, output: 50 }],
|
|
27
|
+
["claude-opus-5-5", { input: 4, output: 20 }],
|
|
28
|
+
["claude-opus-5", { input: 5, output: 25 }],
|
|
29
|
+
["claude-opus-4-8", { input: 5, output: 25 }],
|
|
30
|
+
["claude-opus-4-7", { input: 5, output: 25 }],
|
|
31
|
+
["claude-opus-4-6", { input: 5, output: 25 }],
|
|
32
|
+
["claude-opus-4-5", { input: 5, output: 25 }],
|
|
33
|
+
["claude-sonnet-5", { input: 2, output: 10 }],
|
|
34
|
+
["claude-sonnet-4-6", { input: 3, output: 15 }],
|
|
35
|
+
["claude-sonnet-4-5", { input: 3, output: 15 }],
|
|
36
|
+
["claude-haiku-4-5", { input: 1, output: 5 }]
|
|
37
|
+
]);
|
|
38
|
+
/** Fast mode: the same model at premium rates. */
|
|
39
|
+
const CLAUDE_FAST = new Map([
|
|
40
|
+
["claude-opus-5-5", { input: 8, output: 40 }],
|
|
41
|
+
["claude-opus-5", { input: 10, output: 50 }],
|
|
42
|
+
["claude-opus-4-8", { input: 10, output: 50 }]
|
|
43
|
+
]);
|
|
44
|
+
/** A Codex request whose prompt exceeds this many tokens is billed at its model's long-context tier. */
|
|
45
|
+
export const LONG_PROMPT_TOKENS = 272_000;
|
|
46
|
+
/** Every token of a request's prompt: uncached input, cache reads and cache writes. */
|
|
47
|
+
export const promptTokens = (tokens) => tokens.input + tokens.cacheRead + tokens.cacheWrite5m + tokens.cacheWrite1h;
|
|
48
|
+
/** True when one request's prompt falls in the long-context tier. */
|
|
49
|
+
export const isLongPrompt = (tokens) => promptTokens(tokens) > LONG_PROMPT_TOKENS;
|
|
50
|
+
const flat = (input, output, cacheRead) => ({
|
|
51
|
+
input,
|
|
52
|
+
output,
|
|
53
|
+
cacheRead,
|
|
54
|
+
cacheWrite: null,
|
|
55
|
+
longContext: null
|
|
56
|
+
});
|
|
57
|
+
const tiered = (base, long) => ({
|
|
58
|
+
...base,
|
|
59
|
+
longContext: long
|
|
60
|
+
});
|
|
61
|
+
const rates = (input, output, cacheRead, cacheWrite) => ({
|
|
62
|
+
input,
|
|
63
|
+
output,
|
|
64
|
+
cacheRead,
|
|
65
|
+
cacheWrite
|
|
66
|
+
});
|
|
67
|
+
/** USD per million tokens from the models.dev catalog, for the models Codex runs. */
|
|
68
|
+
const CODEX = new Map([
|
|
69
|
+
["gpt-5", flat(1.25, 10, 0.125)],
|
|
70
|
+
["gpt-5-mini", flat(0.25, 2, 0.025)],
|
|
71
|
+
["gpt-5-nano", flat(0.05, 0.4, 0.005)],
|
|
72
|
+
["gpt-5.1", flat(1.25, 10, 0.125)],
|
|
73
|
+
["gpt-5.2", flat(1.75, 14, 0.175)],
|
|
74
|
+
["gpt-5.3-codex", flat(1.75, 14, 0.175)],
|
|
75
|
+
["gpt-5.3-codex-spark", flat(1.75, 14, 0.175)],
|
|
76
|
+
["gpt-5.4", tiered(rates(2.5, 15, 0.25, null), rates(5, 22.5, 0.5, null))],
|
|
77
|
+
["gpt-5.4-mini", flat(0.75, 4.5, 0.075)],
|
|
78
|
+
["gpt-5.4-nano", flat(0.2, 1.25, 0.02)],
|
|
79
|
+
["gpt-5.5", tiered(rates(5, 30, 0.5, null), rates(10, 45, 1, null))],
|
|
80
|
+
["gpt-5.6", tiered(rates(4, 20, 0.4, 5), rates(8, 30, 0.8, 10))],
|
|
81
|
+
["gpt-5.6-luna", tiered(rates(0.2, 1.2, 0.02, 0.25), rates(0.4, 1.8, 0.04, 0.5))],
|
|
82
|
+
["gpt-5.6-sol", tiered(rates(4, 20, 0.4, 5), rates(8, 30, 0.8, 10))],
|
|
83
|
+
["gpt-5.6-terra", tiered(rates(2, 12, 0.2, 2.5), rates(4, 18, 0.4, 5))],
|
|
84
|
+
["gpt-6-astra", tiered(rates(10, 50, 1, 12.5), rates(20, 75, 2, 25))],
|
|
85
|
+
["gpt-6-luna", tiered(rates(0.1, 0.5, 0.01, 0.125), rates(0.2, 0.75, 0.02, 0.25))],
|
|
86
|
+
["gpt-6-sol", tiered(rates(2, 10, 0.2, 2.5), rates(4, 15, 0.4, 5))],
|
|
87
|
+
["gpt-6.1-sol", tiered(rates(2, 10, 0.1, 2.5), rates(4, 15, 0.2, 5))]
|
|
88
|
+
]);
|
|
89
|
+
/** `claude-opus-5[1m]` and `claude-opus-5-20260101` both price as `claude-opus-5`. */
|
|
90
|
+
export const normalizeClaudeModel = (model) => model.replace(/\[[^\]]*\]$/u, "").replace(/-\d{8}$/u, "");
|
|
91
|
+
const claudeCost = (model, fast, tokens) => {
|
|
92
|
+
const id = normalizeClaudeModel(model);
|
|
93
|
+
// A fast request without a fast rate is unpriced, not priced at the standard rate.
|
|
94
|
+
const found = fast ? CLAUDE_FAST.get(id) : CLAUDE_STANDARD.get(id);
|
|
95
|
+
if (found === undefined)
|
|
96
|
+
return Option.none();
|
|
97
|
+
const input = tokens.input +
|
|
98
|
+
tokens.cacheWrite5m * CLAUDE_CACHE_WRITE_5M +
|
|
99
|
+
tokens.cacheWrite1h * CLAUDE_CACHE_WRITE_1H +
|
|
100
|
+
tokens.cacheRead * CLAUDE_CACHE_READ;
|
|
101
|
+
return Option.some((input * found.input + (tokens.output + tokens.reasoning) * found.output) / PER_MTOK);
|
|
102
|
+
};
|
|
103
|
+
const codexCost = (model, longPrompt, tokens) => {
|
|
104
|
+
const price = CODEX.get(model);
|
|
105
|
+
if (price === undefined)
|
|
106
|
+
return Option.none();
|
|
107
|
+
const cacheWrite = tokens.cacheWrite5m + tokens.cacheWrite1h;
|
|
108
|
+
const applied = longPrompt && price.longContext !== null ? price.longContext : price;
|
|
109
|
+
const cacheReadUsd = tokens.cacheRead === 0
|
|
110
|
+
? 0
|
|
111
|
+
: applied.cacheRead === null
|
|
112
|
+
? null
|
|
113
|
+
: tokens.cacheRead * applied.cacheRead;
|
|
114
|
+
const cacheWriteUsd = cacheWrite === 0 ? 0 : applied.cacheWrite === null ? null : cacheWrite * applied.cacheWrite;
|
|
115
|
+
if (cacheReadUsd === null || cacheWriteUsd === null)
|
|
116
|
+
return Option.none();
|
|
117
|
+
return Option.some((tokens.input * applied.input + cacheReadUsd + cacheWriteUsd +
|
|
118
|
+
(tokens.output + tokens.reasoning) * applied.output) / PER_MTOK);
|
|
119
|
+
};
|
|
120
|
+
/** A group's cost at current list price, or none when its model or a needed rate is unpriced. */
|
|
121
|
+
export const groupCost = (group) => group.agent === "claude"
|
|
122
|
+
? claudeCost(group.model, group.fast, group.tokens)
|
|
123
|
+
: codexCost(group.model, group.longPrompt, group.tokens);
|
|
124
|
+
/** One request's cost at current list price, or none when its model or a needed rate is unpriced. */
|
|
125
|
+
export const apiEquivalentCost = (request) => groupCost({ ...request, longPrompt: isLongPrompt(request.tokens) });
|
|
126
|
+
//# sourceMappingURL=Pricing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Pricing.js","sourceRoot":"","sources":["../../src/core/Pricing.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the transcript and rollout readers share: the line they are handed and the result they give
|
|
3
|
+
* back. Readers are pure, so a chunk of a file and the state carried from the previous chunk are all
|
|
4
|
+
* they need; file access and cursors live in the ingest pass.
|
|
5
|
+
*
|
|
6
|
+
* @module
|
|
7
|
+
*/
|
|
8
|
+
import type { BalanceReading, LimitSnapshot, UsageEvent } from "./Model.js";
|
|
9
|
+
/** One complete line of a source file and the byte offset it starts at. */
|
|
10
|
+
export interface SourceLine {
|
|
11
|
+
readonly offset: number;
|
|
12
|
+
readonly text: string;
|
|
13
|
+
}
|
|
14
|
+
/** Which source file a chunk came from, as the reader stamps it onto what it records. */
|
|
15
|
+
export interface SourceFile {
|
|
16
|
+
/** The file's path relative to its source root, unique per Agent. */
|
|
17
|
+
readonly fileKey: string;
|
|
18
|
+
readonly machine: string;
|
|
19
|
+
readonly sessionId: string;
|
|
20
|
+
}
|
|
21
|
+
/** How many lines that looked relevant were not recorded, by reason. */
|
|
22
|
+
export interface SkipCounts {
|
|
23
|
+
readonly unparseableLine: number;
|
|
24
|
+
readonly missingTimestamp: number;
|
|
25
|
+
/** Lines too long to hold in memory, skipped whole. */
|
|
26
|
+
readonly oversizedLine: number;
|
|
27
|
+
}
|
|
28
|
+
export type SkipReason = keyof SkipCounts;
|
|
29
|
+
export declare const noSkips: SkipCounts;
|
|
30
|
+
export interface ReadResult<State> {
|
|
31
|
+
readonly events: ReadonlyArray<UsageEvent>;
|
|
32
|
+
readonly snapshots: ReadonlyArray<LimitSnapshot>;
|
|
33
|
+
readonly balances: ReadonlyArray<BalanceReading>;
|
|
34
|
+
readonly skipped: SkipCounts;
|
|
35
|
+
readonly state: State;
|
|
36
|
+
}
|
|
37
|
+
/** Adds one skip to a count, returning a new count. */
|
|
38
|
+
export declare const countSkip: (counts: SkipCounts, reason: SkipReason) => SkipCounts;
|
|
39
|
+
/** Sums two skip counts. */
|
|
40
|
+
export declare const mergeSkips: (left: SkipCounts, right: SkipCounts) => SkipCounts;
|
|
41
|
+
/** Epoch milliseconds of an ISO timestamp, or null when it does not parse. */
|
|
42
|
+
export declare const parseInstant: (iso: string) => number | null;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export const noSkips = { unparseableLine: 0, missingTimestamp: 0, oversizedLine: 0 };
|
|
2
|
+
/** Adds one skip to a count, returning a new count. */
|
|
3
|
+
export const countSkip = (counts, reason) => ({
|
|
4
|
+
...counts,
|
|
5
|
+
[reason]: counts[reason] + 1
|
|
6
|
+
});
|
|
7
|
+
/** Sums two skip counts. */
|
|
8
|
+
export const mergeSkips = (left, right) => ({
|
|
9
|
+
unparseableLine: left.unparseableLine + right.unparseableLine,
|
|
10
|
+
missingTimestamp: left.missingTimestamp + right.missingTimestamp,
|
|
11
|
+
oversizedLine: left.oversizedLine + right.oversizedLine
|
|
12
|
+
});
|
|
13
|
+
/** Epoch milliseconds of an ISO timestamp, or null when it does not parse. */
|
|
14
|
+
export const parseInstant = (iso) => {
|
|
15
|
+
const millis = Date.parse(iso);
|
|
16
|
+
return Number.isFinite(millis) ? millis : null;
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=Readers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Readers.js","sourceRoot":"","sources":["../../src/core/Readers.ts"],"names":[],"mappings":"AAiCA,MAAM,CAAC,MAAM,OAAO,GAAe,EAAE,eAAe,EAAE,CAAC,EAAE,gBAAgB,EAAE,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE,CAAA;AAUhG,uDAAuD;AACvD,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,MAAkB,EAAE,MAAkB,EAAc,EAAE,CAAC,CAAC;IAChF,GAAG,MAAM;IACT,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC;CAC7B,CAAC,CAAA;AAEF,4BAA4B;AAC5B,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,IAAgB,EAAE,KAAiB,EAAc,EAAE,CAAC,CAAC;IAC9E,eAAe,EAAE,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC,eAAe;IAC7D,gBAAgB,EAAE,IAAI,CAAC,gBAAgB,GAAG,KAAK,CAAC,gBAAgB;IAChE,aAAa,EAAE,IAAI,CAAC,aAAa,GAAG,KAAK,CAAC,aAAa;CACxD,CAAC,CAAA;AAEF,8EAA8E;AAC9E,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,GAAW,EAAiB,EAAE;IACzD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAC9B,OAAO,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAA;AAChD,CAAC,CAAA"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turning stored facts into what the graphs draw: usage per local period and Booking, and limit
|
|
3
|
+
* series as steps.
|
|
4
|
+
*
|
|
5
|
+
* **Mental model**
|
|
6
|
+
*
|
|
7
|
+
* - **Periods are the viewer's.** The browser sends its IANA zone; hours, days and weeks start at
|
|
8
|
+
* local midnight (weeks on Monday), so a daylight-saving day is 23 or 25 hours long. Each stored
|
|
9
|
+
* 15-minute bucket belongs to exactly one period, because every zone offset is a multiple of 15.
|
|
10
|
+
* - **Bookings and costs are derived here** (ADR 0002), from Attribution Inputs, the Known
|
|
11
|
+
* Projects and today's prices. Typed keys of unknown projects are counted by prefix, not hidden.
|
|
12
|
+
* Unpriced tokens are carried beside the cost, never folded into it as zero.
|
|
13
|
+
* - **A step line needs its starting value.** Each limit series begins at the range's left edge
|
|
14
|
+
* with the reading in force there, taken from the last snapshot before it.
|
|
15
|
+
*
|
|
16
|
+
* @module
|
|
17
|
+
*/
|
|
18
|
+
import { Effect } from "effect";
|
|
19
|
+
import type { LimitsReport, Period, UsageReport } from "../shared/contracts.js";
|
|
20
|
+
import type { LimitSnapshot, TicketTitleValue } from "./Model.js";
|
|
21
|
+
import { type Range, type UsageGroup } from "./Store.js";
|
|
22
|
+
export type PeriodBucket = "hour" | "day" | "week";
|
|
23
|
+
declare const UnknownTimeZone_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
24
|
+
readonly _tag: "UnknownTimeZone";
|
|
25
|
+
} & Readonly<A>;
|
|
26
|
+
export declare class UnknownTimeZone extends UnknownTimeZone_base<{
|
|
27
|
+
readonly zone: string;
|
|
28
|
+
}> {
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Accepts any zone `Intl.DateTimeFormat` accepts, aliases and `Etc/GMT+5` included. The list
|
|
32
|
+
* `Intl.supportedValuesOf` returns is narrower than what the formatter takes, and a browser sends
|
|
33
|
+
* whatever its own runtime resolved.
|
|
34
|
+
*/
|
|
35
|
+
export declare const checkTimeZone: (zone: string) => Effect.Effect<string, UnknownTimeZone>;
|
|
36
|
+
/** Every local period the range touches, each with the first 15-minute instant inside the range. */
|
|
37
|
+
export declare const periodsOf: (options: {
|
|
38
|
+
readonly from: number;
|
|
39
|
+
readonly to: number;
|
|
40
|
+
readonly timeZone: string;
|
|
41
|
+
readonly bucket: PeriodBucket;
|
|
42
|
+
}) => ReadonlyArray<Period>;
|
|
43
|
+
/**
|
|
44
|
+
* Usage per period and Booking. `end` bounds the last period; groups outside every period are
|
|
45
|
+
* dropped. Titles are looked up by ticket key.
|
|
46
|
+
*/
|
|
47
|
+
export declare const buildUsageReport: (groups: ReadonlyArray<UsageGroup>, periods: ReadonlyArray<Period>, titles: Readonly<Record<string, TicketTitleValue>>, projects: ReadonlySet<string>, end?: number) => UsageReport;
|
|
48
|
+
/**
|
|
49
|
+
* Limit series over a range, from every snapshot up to its end in time order. Each series starts
|
|
50
|
+
* at `from` with the reading in force there when one exists.
|
|
51
|
+
*/
|
|
52
|
+
export declare const buildLimitsReport: (snapshots: ReadonlyArray<LimitSnapshot>, range: Range) => Omit<LimitsReport, "balances">;
|
|
53
|
+
export {};
|