Skip to content

Commit d475794

Browse files
asizikovCopilot
andcommitted
feat: rename AI credit input fields
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 011f724 commit d475794

4 files changed

Lines changed: 36 additions & 28 deletions

File tree

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ npm run preview
6060
3. The app will parse the report and compare request-based and AI Credits-based billing signals
6161
4. Explore the visualizations and data views to preview how future bills may look after the AI Credits transition
6262

63-
Sample reports are available in the `examples/` directory. Older examples without `aiu_quantity` still load, but AI Credits will remain `0` for those rows.
63+
Sample reports are available in the `examples/` directory. Older examples without `aic_quantity` still load, but AI Credits will remain `0` for those rows.
6464

6565
## CSV Report Format
6666

@@ -81,9 +81,10 @@ The application expects CSV files with the following primary columns:
8181
- `total_monthly_quota` - Monthly quota amount
8282
- `organization` - Organization slug
8383
- `cost_center_name` - Optional cost center identifier
84-
- `aiu_quantity` - AI Credits for the same usage row
84+
- `aic_quantity` - AI Credits for the same usage row
85+
- `aic_gross_amount` - AI Credit gross cost for the same usage row
8586

86-
The viewer now supports both billing units. On `requests` rows, `quantity` and `applied_cost_per_quantity` drive PRU-based calculations, while `aiu_quantity` and `aiu_gross_amount` remain comparison values and stay `0` when those AI Credit fields are absent. On `ai-units` rows, Premium Requests and generic PRU gross/discount/net totals are counted as `0`; AI Credits are counted from the AI-specific credit fields (`aiu_quantity`, falling back to `quantity` only when the AI field is absent) and AI Credit gross cost comes from `aiu_gross_amount` (falling back to `gross_amount` only when the AI field is absent). Legacy token columns may still appear in older exports, but they are no longer used by the app.
87+
The viewer now supports both billing units. On `requests` rows, `quantity` and `applied_cost_per_quantity` drive PRU-based calculations, while `aic_quantity` and `aic_gross_amount` remain comparison values and stay `0` when those AI Credit fields are absent. On `ai-units` rows, Premium Requests and generic PRU gross/discount/net totals are counted as `0`; AI Credits are counted from the AI-specific credit fields (`aic_quantity`, falling back to `quantity` only when the AI field is absent) and AI Credit gross cost comes from `aic_gross_amount` (falling back to `gross_amount` only when the AI field is absent). Legacy `aiu_*` column names from older exports are still accepted during parsing, but they are no longer the documented format.
8788

8889
See [docs/report-format.md](docs/report-format.md) for detailed format specifications.
8990

docs/report-format.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,21 +21,22 @@ Source examples: files in `examples/`. Some existing sample exports still use th
2121
| `total_monthly_quota` | number | Monthly quota for the user or plan applicable to the row. |
2222
| `organization` | string | Organization slug associated with the usage. |
2323
| `cost_center_name` | string | Optional cost center or tagging field. |
24-
| `aiu_quantity` | number | Same usage converted to AI Credits. |
24+
| `aic_quantity` | number | Same usage converted to AI Credits. |
2525

2626
## Additional columns
2727

2828
| Column | Status | Notes |
2929
| --- | --- | --- |
30-
| `aiu_gross_amount` | Present in newer previews | Used for AI Credit gross cost when present. |
30+
| `aic_gross_amount` | Present in newer previews | Used for AI Credit gross cost when present. |
3131
| `total_input_tokens` | Legacy / optional | Older token-oriented export field; ignored by the app. |
3232
| `total_output_tokens` | Legacy / optional | Older token-oriented export field; ignored by the app. |
3333
| `total_cache_creation_tokens` | Legacy / optional | Older token-oriented export field; ignored by the app. |
3434
| `total_cache_read_tokens` | Legacy / optional | Older token-oriented export field; ignored by the app. |
3535

3636
## Notes
3737
- Header row is single-lined; subsequent rows are usage records. Values are comma-separated, double-quoted where needed.
38-
- Monetary fields are decimals. `aiu_quantity` may also be fractional.
38+
- Monetary fields are decimals. `aic_quantity` may also be fractional.
3939
- The app streams the file, treats the first row as the header, and counts subsequent data rows without loading the entire file into memory.
40-
- Current calculations support both row types: on `requests` rows, `quantity` and `applied_cost_per_quantity` describe PRU usage while `aiu_quantity` and `aiu_gross_amount` remain reference/comparison values and stay `0` when the AI Credit fields are absent; on `ai-units` rows, Premium Requests and generic PRU gross/discount/net totals are counted as `0`, while AI Credits and AI Credit gross cost are taken from the AI-specific fields (`aiu_quantity` / `aiu_gross_amount`, with fallback to `quantity` / `gross_amount` only when those AI-specific fields are absent).
41-
- Reports without `aiu_quantity` still load, but AI Credit summaries will display `0`.
40+
- Current calculations support both row types: on `requests` rows, `quantity` and `applied_cost_per_quantity` describe PRU usage while `aic_quantity` and `aic_gross_amount` remain reference/comparison values and stay `0` when those AI Credit fields are absent; on `ai-units` rows, Premium Requests and generic PRU gross/discount/net totals are counted as `0`, while AI Credits and AI Credit gross cost are taken from the AI-specific fields (`aic_quantity` / `aic_gross_amount`, with fallback to `quantity` / `gross_amount` only when those AI-specific fields are absent).
41+
- Reports without `aic_quantity` still load, but AI Credit summaries will display `0`.
42+
- Older exports that still use legacy `aiu_quantity` / `aiu_gross_amount` headers are accepted as parser fallbacks.

src/pipeline/parser.ts

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@ export type TokenUsageRecord = {
2424
total_monthly_quota: number
2525
organization: string
2626
cost_center_name: string | null
27-
aiu_quantity: number
28-
aiu_gross_amount: number
27+
aic_quantity: number
28+
aic_gross_amount: number
2929
aic_net_amount: number
30-
has_aiu_quantity: boolean
31-
has_aiu_gross_amount: boolean
30+
has_aic_quantity: boolean
31+
has_aic_gross_amount: boolean
3232
}
3333

3434
export type TokenUsageHeader = {
@@ -57,14 +57,14 @@ export type UsageMetrics = {
5757
export function getAicUsageMetrics(record: TokenUsageRecord): Pick<UsageMetrics, 'aiuQuantity' | 'aiuGrossAmount'> {
5858
if (isAiUnitUsageRecord(record.unit_type)) {
5959
return {
60-
aiuQuantity: record.has_aiu_quantity ? record.aiu_quantity : record.quantity,
61-
aiuGrossAmount: record.has_aiu_gross_amount ? record.aiu_gross_amount : record.gross_amount,
60+
aiuQuantity: record.has_aic_quantity ? record.aic_quantity : record.quantity,
61+
aiuGrossAmount: record.has_aic_gross_amount ? record.aic_gross_amount : record.gross_amount,
6262
}
6363
}
6464

6565
return {
66-
aiuQuantity: record.aiu_quantity,
67-
aiuGrossAmount: record.aiu_gross_amount,
66+
aiuQuantity: record.aic_quantity,
67+
aiuGrossAmount: record.aic_gross_amount,
6868
}
6969
}
7070

@@ -173,10 +173,16 @@ function getNullableString(row: string[], i: number | undefined): string | null
173173
return raw ? raw : null
174174
}
175175

176+
function getOptionalHeaderIndex(header: TokenUsageHeader, ...names: string[]): number | undefined {
177+
return names.map((name) => header.index[name]).find((index) => index !== undefined)
178+
}
179+
176180
export function parseTokenUsageRecord(line: string, header: TokenUsageHeader): TokenUsageRecord {
177181
const row = parseCsvRow(line)
178-
const aiuQuantityRaw = getString(row, header.index['aiu_quantity']).trim()
179-
const aiuGrossAmountRaw = getString(row, header.index['aiu_gross_amount']).trim()
182+
const aicQuantityIndex = getOptionalHeaderIndex(header, 'aic_quantity', 'aiu_quantity')
183+
const aicGrossAmountIndex = getOptionalHeaderIndex(header, 'aic_gross_amount', 'aiu_gross_amount')
184+
const aicQuantityRaw = getString(row, aicQuantityIndex).trim()
185+
const aicGrossAmountRaw = getString(row, aicGrossAmountIndex).trim()
180186

181187
const record: TokenUsageRecord = {
182188
date: getString(row, header.index['date']).trim(),
@@ -194,11 +200,11 @@ export function parseTokenUsageRecord(line: string, header: TokenUsageHeader): T
194200
total_monthly_quota: getNumber(row, header.index['total_monthly_quota']),
195201
organization: getString(row, header.index['organization']).trim(),
196202
cost_center_name: getNullableString(row, header.index['cost_center_name']),
197-
aiu_quantity: getNumber(row, header.index['aiu_quantity']),
198-
aiu_gross_amount: getNumber(row, header.index['aiu_gross_amount']),
203+
aic_quantity: getNumber(row, aicQuantityIndex),
204+
aic_gross_amount: getNumber(row, aicGrossAmountIndex),
199205
aic_net_amount: 0,
200-
has_aiu_quantity: aiuQuantityRaw !== '',
201-
has_aiu_gross_amount: aiuGrossAmountRaw !== '',
206+
has_aic_quantity: aicQuantityRaw !== '',
207+
has_aic_gross_amount: aicGrossAmountRaw !== '',
202208
}
203209

204210
record.aic_net_amount = getAicUsageMetrics(record).aiuGrossAmount

src/views/ReportGuideView.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ const HEADER = [
1414
'total_monthly_quota',
1515
'organization',
1616
'cost_center_name',
17-
'aiu_quantity',
18-
'aiu_gross_amount',
17+
'aic_quantity',
18+
'aic_gross_amount',
1919
]
2020

2121
const SAMPLE_ROW_1 = [
@@ -87,12 +87,12 @@ const SAMPLE_ROW_2_ANNOTATIONS: { index: number; label: string; note: string }[]
8787
},
8888
{
8989
index: 15,
90-
label: 'aiu_quantity',
90+
label: 'aic_quantity',
9191
note: '≈ 24.9 AICs consumed. Under token-based billing, even Base Models consume AI Credits proportional to actual token usage.',
9292
},
9393
{
9494
index: 16,
95-
label: 'aiu_gross_amount',
95+
label: 'aic_gross_amount',
9696
note: '≈ $0.249 (24.9 AICs × $0.01). This is what the same usage would cost under token-based billing.',
9797
},
9898
]
@@ -135,12 +135,12 @@ const SAMPLE_ROW_1_ANNOTATIONS: { index: number; label: string; note: string }[]
135135
{ index: 14, label: 'cost_center_name', note: '"Octocats" — the cost center this user is assigned to.' },
136136
{
137137
index: 15,
138-
label: 'aiu_quantity',
138+
label: 'aic_quantity',
139139
note: '≈ 3.107 AICs — the token-based billing equivalent of this request. AICs represent the actual tokens consumed.',
140140
},
141141
{
142142
index: 16,
143-
label: 'aiu_gross_amount',
143+
label: 'aic_gross_amount',
144144
note: '≈ $0.031 (3.107 AICs × $0.01). Under token-based billing this request would cost roughly $0.03.',
145145
},
146146
]

0 commit comments

Comments
 (0)