Skip to content

Commit 1100b6a

Browse files
asizikovCopilot
andcommitted
fix: address PR review feedback
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 036a26e commit 1100b6a

9 files changed

Lines changed: 45 additions & 24 deletions

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ npm run preview
6666
3. The app will parse the report and compare request-based and AI Credits-based billing signals
6767
4. Explore the visualizations and data views to preview how future bills may look after the AI Credits transition
6868

69-
If you don't have your own data yet, you can use any GitHub Copilot billing CSV export that matches the current format, including the `aic_quantity` and `aic_gross_amount` columns.
69+
If you don't have your own data yet, export a GitHub Copilot billing CSV from your organization that matches the current format, including the `aic_quantity` and `aic_gross_amount` columns.
7070

7171
## CSV Report Format
7272

src/pipeline/aggregators/aicDiscountAggregation.test.ts

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { describe, expect, it } from 'vitest'
22
import type { TokenUsageRecord } from '../parser'
3-
import { calculateAicDiscountAmount, calculateSavingsDifference } from '../../views/billingComparison'
3+
import { calculateAicDiscountAmount, calculateSavingsDifference } from '../../utils/billingComparison'
44
import { CostCenterAggregator } from './costCenterAggregator'
55
import { DailyUsageAggregator } from './dailyUsageAggregator'
66
import { ModelUsageAggregator } from './modelUsageAggregator'
@@ -61,15 +61,12 @@ function sum(values: number[]): number {
6161
describe('derived AIC discount aggregation', () => {
6262
it('derives organization AIC discount as gross minus net', () => {
6363
const result = aggregate([createRecord({ aic_gross_amount: 1.5, aic_net_amount: 0.6 })])
64+
const organization = result.organizations.organizations[0]
6465

65-
expect(result.organizations.organizations).toEqual([
66-
expect.objectContaining({
67-
organization: 'octo',
68-
aicGrossAmount: 1.5,
69-
aicNetAmount: 0.6,
70-
aicDiscountAmount: 0.9,
71-
}),
72-
])
66+
expect(organization.organization).toBe('octo')
67+
expect(organization.aicGrossAmount).toBeCloseTo(1.5)
68+
expect(organization.aicNetAmount).toBeCloseTo(0.6)
69+
expect(organization.aicDiscountAmount).toBeCloseTo(0.9)
7370
})
7471

7572
it('derives cost-center AIC discount from totals gross minus net', () => {

src/pipeline/aggregators/pruAggregation.test.ts

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,10 @@ function sumNumbers(values: number[]): number {
103103
return values.reduce((total, value) => total + value, 0)
104104
}
105105

106+
function expectCloseMoney(actual: number, expected: number): void {
107+
expect(actual).toBeCloseTo(expected)
108+
}
109+
106110
function expectNetEquation(totals: { grossAmount: number; discountAmount: number; netAmount: number }): void {
107111
expect(totals.grossAmount - totals.discountAmount).toBeCloseTo(totals.netAmount)
108112
}
@@ -180,23 +184,23 @@ describe('PRU gross/net/discount aggregation', () => {
180184
dailyDiscount: userDiscount,
181185
})
182186

183-
expect(result.organizations.organizations).toEqual([
184-
expect.objectContaining({
185-
organization: 'octo',
186-
requests: 23,
187-
grossAmount: 7,
188-
discountAmount: 1.7,
189-
netAmount: 5.3,
190-
}),
191-
])
187+
const organization = result.organizations.organizations[0]
188+
expect(organization.organization).toBe('octo')
189+
expect(organization.requests).toBe(23)
190+
expectCloseMoney(organization.grossAmount, 7)
191+
expectCloseMoney(organization.discountAmount, 1.7)
192+
expectCloseMoney(organization.netAmount, 5.3)
192193
})
193194

194195
it('keeps cost-center totals aligned with per-model and per-user rollups', () => {
195196
const result = aggregate(createFixture())
196197
const cats = result.costCenters.costCenters.find((costCenter) => costCenter.costCenterName === 'Cats')
197198

198199
expect(cats).toBeDefined()
199-
expect(cats?.totals).toEqual(expect.objectContaining({ requests: 15, grossAmount: 3, discountAmount: 0.7, netAmount: 2.3 }))
200+
expect(cats?.totals.requests).toBe(15)
201+
expectCloseMoney(cats!.totals.grossAmount, 3)
202+
expectCloseMoney(cats!.totals.discountAmount, 0.7)
203+
expectCloseMoney(cats!.totals.netAmount, 2.3)
200204
expect(sumNumbers(Object.values(cats?.totalsByModel ?? {}).map((totals) => totals.grossAmount))).toBeCloseTo(cats!.totals.grossAmount)
201205
expect(sumNumbers(Object.values(cats?.totalsByModel ?? {}).map((totals) => totals.netAmount))).toBeCloseTo(cats!.totals.netAmount)
202206
expect(sumNumbers(Object.values(cats?.totalsByUser ?? {}).map((totals) => totals.grossAmount))).toBeCloseTo(cats!.totals.grossAmount)
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { describe, expect, it } from 'vitest'
2+
import { calculateAicDiscountAmount, calculateSavingsDifference } from './billingComparison'
3+
4+
describe('billing comparison helpers', () => {
5+
it('derives AIC discount as gross minus net for fully covered usage', () => {
6+
expect(calculateAicDiscountAmount(1.2, 0)).toBeCloseTo(1.2)
7+
})
8+
9+
it('derives AIC discount as 0 for uncovered usage', () => {
10+
expect(calculateAicDiscountAmount(1.2, 1.2)).toBeCloseTo(0)
11+
})
12+
13+
it('derives AIC discount as only the covered portion for partial usage', () => {
14+
expect(calculateAicDiscountAmount(1.2, 0.45)).toBeCloseTo(0.75)
15+
})
16+
17+
it('derives displayed savings as PRU net minus AIC net', () => {
18+
expect(calculateSavingsDifference(5.3, 1.1)).toBeCloseTo(4.2)
19+
})
20+
})

src/views/CostCentersView.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useMemo, useState } from 'react'
22
import type { CostCenterResult } from '../pipeline/aggregators/costCenterAggregator'
3+
import { calculateAicDiscountAmount, calculateSavingsDifference } from '../utils/billingComparison'
34
import { formatAic, formatDifference, formatUsd } from '../utils/format'
4-
import { calculateAicDiscountAmount, calculateSavingsDifference } from './billingComparison'
55

66
export function CostCentersView({ data, rangeStart }: { data: CostCenterResult; rangeStart?: string | null }) {
77
const [selected, setSelected] = useState<string>(data.costCenters[0]?.costCenterName ?? '')

src/views/ModelsView.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { useState, useMemo } from 'react'
22
import type { ModelUsageResult, ModelDailyUsageData } from '../pipeline/aggregators/modelUsageAggregator'
33
import { DualAxisLineChart, MultiSeriesStackedBarChart } from '../components'
4+
import { calculateAicDiscountAmount, calculateSavingsDifference } from '../utils/billingComparison'
45
import { fillDataForRange } from '../utils/fillDataForRange'
56
import { formatAic, formatUsd } from '../utils/format'
6-
import { calculateAicDiscountAmount, calculateSavingsDifference } from './billingComparison'
77

88
function createEmptyModelDailyUsage(date: string): ModelDailyUsageData {
99
return {

src/views/OrganizationsView.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { OrganizationResult } from '../pipeline/aggregators/organizationAggregator'
22
import { formatAic, formatDifference, formatUsd } from '../utils/format'
3-
import { calculateSavingsDifference } from './billingComparison'
3+
import { calculateSavingsDifference } from '../utils/billingComparison'
44

55
export function OrganizationsView({ data }: { data: OrganizationResult }) {
66
if (data.organizations.length === 0) {

src/views/UserDetailsView.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { useMemo } from 'react'
22
import { MultiSeriesStackedBarChart } from '../components'
33
import type { UserDailyUsage, UserUsage } from '../pipeline/aggregators/userUsageAggregator'
4+
import { calculateAicDiscountAmount, calculateSavingsDifference } from '../utils/billingComparison'
45
import { fillDataForRange } from '../utils/fillDataForRange'
56
import { formatAic } from '../utils/format'
6-
import { calculateAicDiscountAmount, calculateSavingsDifference } from './billingComparison'
77

88
type DailySummaryModelRow = {
99
model: string

0 commit comments

Comments
 (0)