Skip to content
Open
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
84 changes: 84 additions & 0 deletions packages/js/src/__tests__/calcPrice.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,90 @@ describe('Core Price Calculation Function', () => {
})
})

it('should handle web search requests as total cost', () => {
const usage: Usage = {
input_tokens: 1000,
output_tokens: 500,
tool_use: { web_search: 3 },
}
const modelPrice: ModelPrice = {
input_mtok: 3.0,
output_mtok: 15.0,
tool_use_kcount: { web_search: 10 }, // $10 per 1000 web searches
}

const result = calcPrice(usage, modelPrice)

expect(result).toMatchObject({
input_price: 0.003, // 1000 * 3.0 / 1_000_000
output_price: 0.0075, // 500 * 15.0 / 1_000_000
total_price: 0.003 + 0.0075 + (10 * 3) / 1000, // add web search cost to total only
})
})

it('should handle file search requests as total cost', () => {
const usage: Usage = {
input_tokens: 1000,
output_tokens: 500,
tool_use: { file_search: 4 },
}
const modelPrice: ModelPrice = {
input_mtok: 2.5,
output_mtok: 10.0,
tool_use_kcount: { file_search: 2.5 }, // $2.50 per 1000 file searches
}

const result = calcPrice(usage, modelPrice)

expect(result).toMatchObject({
input_price: 0.0025, // 1000 * 2.5 / 1_000_000
output_price: 0.005, // 500 * 10.0 / 1_000_000
total_price: 0.0025 + 0.005 + (2.5 * 4) / 1000, // add file search cost to total only
})
})

it('should not add file search cost when requests is zero', () => {
const usage: Usage = {
input_tokens: 1000,
output_tokens: 500,
tool_use: { file_search: 0 },
}
const modelPrice: ModelPrice = {
input_mtok: 2.5,
output_mtok: 10.0,
tool_use_kcount: { file_search: 2.5 },
}

const result = calcPrice(usage, modelPrice)

expect(result).toMatchObject({
input_price: 0.0025,
output_price: 0.005,
total_price: 0.0025 + 0.005,
})
})

it('should not add web search cost when requests is zero', () => {
const usage: Usage = {
input_tokens: 1000,
output_tokens: 500,
tool_use: { web_search: 0 },
}
const modelPrice: ModelPrice = {
input_mtok: 3.0,
output_mtok: 15.0,
tool_use_kcount: { web_search: 10 },
}

const result = calcPrice(usage, modelPrice)

expect(result).toMatchObject({
input_price: 0.003,
output_price: 0.0075,
total_price: 0.003 + 0.0075,
})
})

it.each([
{
expected: { input_price: 0, output_price: 0, total_price: 0 },
Expand Down
2 changes: 1 addition & 1 deletion packages/js/src/__tests__/dataset.test.ts
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚩 Pre-existing date mismatch between JS dataset test and Python dataset generation

The JS dataset test at packages/js/src/__tests__/dataset.test.ts:89 uses new Date(2025, 11, 6, 12, 0, 0) which is December 6, 2025 in local time (JS months are 0-indexed). The Python dataset generation at tests/dataset/extract_usages.py:115 uses datetime.datetime(2025, 11, 6, 12, 0, 0, tzinfo=datetime.timezone.utc) which is November 6, 2025 in UTC.

This is a pre-existing discrepancy (not introduced by this PR). It doesn't currently cause test failures because all date-sensitive price constraints (like the o3 model's start_date of 2025-06-10) resolve to the same price tier for both dates. However, if a future price change has a start_date between November and December 2025, the JS and Python tests would compute different prices for the same dataset row.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ describe('dataset', () => {
}
for (const key of Object.keys(extracted.usage)) {
const k = key as keyof Usage
expect(extractedUsage[k]).toBe(extracted.usage[k])
expect(extractedUsage[k]).toStrictEqual(extracted.usage[k])
}
}
}
Expand Down
91 changes: 91 additions & 0 deletions packages/js/src/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ export const data: Provider[] = [
dest: 'output_tokens',
required: true,
},
{
path: ['server_tool_use', 'web_search_requests'],
dest: 'web_search',
required: false,
},
],
},
{
Expand Down Expand Up @@ -115,6 +120,9 @@ export const data: Provider[] = [
cache_write_mtok: 1,
cache_read_mtok: 0.08,
output_mtok: 4,
tool_use_kcount: {
web_search: 10,
},
},
},
{
Expand All @@ -138,6 +146,9 @@ export const data: Provider[] = [
cache_write_mtok: 3.75,
cache_read_mtok: 0.3,
output_mtok: 15,
tool_use_kcount: {
web_search: 10,
},
},
},
{
Expand Down Expand Up @@ -167,6 +178,9 @@ export const data: Provider[] = [
cache_write_mtok: 3.75,
cache_read_mtok: 0.3,
output_mtok: 15,
tool_use_kcount: {
web_search: 10,
},
},
},
{
Expand All @@ -182,6 +196,9 @@ export const data: Provider[] = [
cache_write_mtok: 0.3,
cache_read_mtok: 0.03,
output_mtok: 1.25,
tool_use_kcount: {
web_search: 10,
},
},
},
{
Expand All @@ -198,6 +215,9 @@ export const data: Provider[] = [
cache_write_mtok: 18.75,
cache_read_mtok: 1.5,
output_mtok: 75,
tool_use_kcount: {
web_search: 10,
},
},
},
{
Expand All @@ -214,6 +234,9 @@ export const data: Provider[] = [
cache_write_mtok: 3.75,
cache_read_mtok: 0.3,
output_mtok: 15,
tool_use_kcount: {
web_search: 10,
},
},
},
{
Expand Down Expand Up @@ -242,6 +265,9 @@ export const data: Provider[] = [
cache_write_mtok: 1.25,
cache_read_mtok: 0.1,
output_mtok: 5,
tool_use_kcount: {
web_search: 10,
},
},
},
{
Expand Down Expand Up @@ -270,6 +296,9 @@ export const data: Provider[] = [
cache_write_mtok: 18.75,
cache_read_mtok: 1.5,
output_mtok: 75,
tool_use_kcount: {
web_search: 10,
},
},
},
{
Expand All @@ -292,6 +321,9 @@ export const data: Provider[] = [
cache_write_mtok: 18.75,
cache_read_mtok: 1.5,
output_mtok: 75,
tool_use_kcount: {
web_search: 10,
},
},
},
{
Expand Down Expand Up @@ -320,6 +352,9 @@ export const data: Provider[] = [
cache_write_mtok: 6.25,
cache_read_mtok: 0.5,
output_mtok: 25,
tool_use_kcount: {
web_search: 10,
},
},
},
{
Expand Down Expand Up @@ -380,6 +415,9 @@ export const data: Provider[] = [
},
],
},
tool_use_kcount: {
web_search: 10,
},
},
},
{
Expand Down Expand Up @@ -411,6 +449,9 @@ export const data: Provider[] = [
cache_write_mtok: 3.75,
cache_read_mtok: 0.3,
output_mtok: 15,
tool_use_kcount: {
web_search: 10,
},
},
},
{
Expand Down Expand Up @@ -465,6 +506,9 @@ export const data: Provider[] = [
},
],
},
tool_use_kcount: {
web_search: 10,
},
},
},
{
Expand Down Expand Up @@ -519,6 +563,9 @@ export const data: Provider[] = [
},
],
},
tool_use_kcount: {
web_search: 10,
},
},
},
{
Expand Down Expand Up @@ -8935,6 +8982,10 @@ export const data: Provider[] = [
input_mtok: 2,
cache_read_mtok: 0.5,
output_mtok: 8,
tool_use_kcount: {
web_search: 30,
file_search: 2.5,
},
},
},
{
Expand All @@ -8957,6 +9008,10 @@ export const data: Provider[] = [
input_mtok: 0.4,
cache_read_mtok: 0.1,
output_mtok: 1.6,
tool_use_kcount: {
web_search: 30,
file_search: 2.5,
},
},
},
{
Expand All @@ -8979,6 +9034,10 @@ export const data: Provider[] = [
input_mtok: 0.1,
cache_read_mtok: 0.025,
output_mtok: 0.4,
tool_use_kcount: {
web_search: 30,
file_search: 2.5,
},
},
},
{
Expand Down Expand Up @@ -9021,6 +9080,10 @@ export const data: Provider[] = [
input_mtok: 2.5,
cache_read_mtok: 1.25,
output_mtok: 10,
tool_use_kcount: {
web_search: 25,
file_search: 2.5,
},
},
},
{
Expand Down Expand Up @@ -9062,6 +9125,10 @@ export const data: Provider[] = [
input_mtok: 0.15,
cache_read_mtok: 0.075,
output_mtok: 0.6,
tool_use_kcount: {
web_search: 25,
file_search: 2.5,
},
},
},
{
Expand Down Expand Up @@ -9215,6 +9282,10 @@ export const data: Provider[] = [
input_mtok: 1.25,
cache_read_mtok: 0.125,
output_mtok: 10,
tool_use_kcount: {
web_search: 30,
file_search: 2.5,
},
},
},
{
Expand Down Expand Up @@ -9260,6 +9331,10 @@ export const data: Provider[] = [
input_mtok: 0.25,
cache_read_mtok: 0.025,
output_mtok: 2,
tool_use_kcount: {
web_search: 30,
file_search: 2.5,
},
},
},
{
Expand All @@ -9281,6 +9356,10 @@ export const data: Provider[] = [
input_mtok: 0.05,
cache_read_mtok: 0.005,
output_mtok: 0.4,
tool_use_kcount: {
web_search: 30,
file_search: 2.5,
},
},
},
{
Expand Down Expand Up @@ -9350,6 +9429,10 @@ export const data: Provider[] = [
input_mtok: 1.25,
cache_read_mtok: 0.125,
output_mtok: 10,
tool_use_kcount: {
web_search: 30,
file_search: 2.5,
},
},
},
{
Expand All @@ -9376,6 +9459,10 @@ export const data: Provider[] = [
input_mtok: 0.25,
cache_read_mtok: 0.025,
output_mtok: 2,
tool_use_kcount: {
web_search: 30,
file_search: 2.5,
},
},
},
{
Expand Down Expand Up @@ -9421,6 +9508,10 @@ export const data: Provider[] = [
input_mtok: 1.75,
cache_read_mtok: 0.175,
output_mtok: 14,
tool_use_kcount: {
web_search: 30,
file_search: 2.5,
},
},
},
{
Expand Down
8 changes: 8 additions & 0 deletions packages/js/src/engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,14 @@ export function calcPrice(usage: Usage, modelPrice: ModelPrice): ModelPriceCalcu
if (modelPrice.requests_kcount !== undefined) {
totalPrice += modelPrice.requests_kcount / 1000
}
if (modelPrice.tool_use_kcount && usage.tool_use) {
for (const [unit, price] of Object.entries(modelPrice.tool_use_kcount)) {
const count = usage.tool_use[unit] ?? 0
if (count) {
totalPrice += (price * count) / 1000
}
}
}

return {
input_price: inputPrice,
Expand Down
Loading