Skip to content

Commit e380a65

Browse files
authored
refactor: merge cost info into modelConfigs (#315)
*Issue #, if available:* *Description of changes:* By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.
1 parent 326205d commit e380a65

File tree

3 files changed

+83
-10
lines changed

3 files changed

+83
-10
lines changed
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { expect, test } from 'vitest';
2+
import { calculateCost } from './cost';
3+
4+
test('calculateCost for sonnet3.7 model', () => {
5+
// GIVEN
6+
const modelId = 'us.anthropic.claude-3-7-sonnet-20250219-v1:0';
7+
const inputTokens = 1000;
8+
const outputTokens = 500;
9+
const cacheReadTokens = 200;
10+
const cacheWriteTokens = 100;
11+
12+
// WHEN
13+
const cost = calculateCost(modelId, inputTokens, outputTokens, cacheReadTokens, cacheWriteTokens);
14+
15+
// THEN
16+
const expectedCost = (1000 * 0.003 + 500 * 0.015 + 200 * 0.0003 + 100 * 0.00375) / 1000;
17+
expect(cost).toBe(expectedCost);
18+
});
19+
20+
test('calculateCost for haiku3.5 model', () => {
21+
// GIVEN
22+
const modelId = 'apac.anthropic.claude-3-5-haiku-20241022-v1:0';
23+
const inputTokens = 2000;
24+
const outputTokens = 1000;
25+
const cacheReadTokens = 500;
26+
const cacheWriteTokens = 250;
27+
28+
// WHEN
29+
const cost = calculateCost(modelId, inputTokens, outputTokens, cacheReadTokens, cacheWriteTokens);
30+
31+
// THEN
32+
const expectedCost = (2000 * 0.0008 + 1000 * 0.004 + 500 * 0.00008 + 250 * 0.001) / 1000;
33+
expect(cost).toBe(expectedCost);
34+
});
35+
36+
test('calculateCost returns 0 for unknown model', () => {
37+
// GIVEN
38+
const modelId = 'unknown-model-id';
39+
const inputTokens = 1000;
40+
const outputTokens = 500;
41+
const cacheReadTokens = 200;
42+
const cacheWriteTokens = 100;
43+
44+
// WHEN
45+
const cost = calculateCost(modelId, inputTokens, outputTokens, cacheReadTokens, cacheWriteTokens);
46+
47+
// THEN
48+
expect(cost).toBe(0);
49+
});
50+
51+
test('calculateCost with zero tokens', () => {
52+
// GIVEN
53+
const modelId = 'anthropic.claude-sonnet-4-20250514-v1:0';
54+
const inputTokens = 0;
55+
const outputTokens = 0;
56+
const cacheReadTokens = 0;
57+
const cacheWriteTokens = 0;
58+
59+
// WHEN
60+
const cost = calculateCost(modelId, inputTokens, outputTokens, cacheReadTokens, cacheWriteTokens);
61+
62+
// THEN
63+
expect(cost).toBe(0);
64+
});

packages/agent-core/src/lib/cost.ts

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,6 @@
11
import { QueryCommand, UpdateCommand } from '@aws-sdk/lib-dynamodb';
22
import { ddb, TableName } from './aws/ddb';
3-
4-
const modelPricing = {
5-
'3-7-sonnet': { input: 0.003, output: 0.015, cacheRead: 0.0003, cacheWrite: 0.00375 },
6-
'3-5-sonnet': { input: 0.003, output: 0.015, cacheRead: 0.0003, cacheWrite: 0.00375 },
7-
'3-5-haiku': { input: 0.0008, output: 0.004, cacheRead: 0.00008, cacheWrite: 0.001 },
8-
'sonnet-4': { input: 0.003, output: 0.015, cacheRead: 0.0003, cacheWrite: 0.00375 },
9-
'opus-4': { input: 0.015, output: 0.075, cacheRead: 0.0015, cacheWrite: 0.01875 },
10-
};
3+
import { modelConfigs } from '../schema/model';
114

125
// Calculate cost in USD based on token usage
136
export const calculateCost = (
@@ -17,8 +10,10 @@ export const calculateCost = (
1710
cacheReadTokens: number,
1811
cacheWriteTokens: number
1912
) => {
20-
const pricing = Object.entries(modelPricing).find(([key]) => modelId.includes(key))?.[1];
21-
if (pricing == null) return 0;
13+
const config = Object.values(modelConfigs).find((config) => modelId.includes(config.modelId));
14+
if (!config) return 0;
15+
16+
const pricing = config.pricing;
2217
return (
2318
(inputTokens * pricing.input +
2419
outputTokens * pricing.output +

packages/agent-core/src/schema/model.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ const modelConfigSchema = z.object({
2222
reasoningSupport: z.boolean(),
2323
toolChoiceSupport: z.array(z.enum(['any', 'auto', 'tool'])),
2424
isHidden: z.boolean().optional(),
25+
pricing: z.object({
26+
input: z.number(),
27+
output: z.number(),
28+
cacheRead: z.number(),
29+
cacheWrite: z.number(),
30+
}),
2531
});
2632

2733
export const modelConfigs: Record<ModelType, z.infer<typeof modelConfigSchema>> = {
@@ -34,6 +40,7 @@ export const modelConfigs: Record<ModelType, z.infer<typeof modelConfigSchema>>
3440
reasoningSupport: false,
3541
toolChoiceSupport: ['any', 'auto', 'tool'],
3642
isHidden: true,
43+
pricing: { input: 0.003, output: 0.015, cacheRead: 0.0003, cacheWrite: 0.00375 },
3744
},
3845
'sonnet3.5': {
3946
name: 'Claude 3.5 Sonnet v2',
@@ -43,6 +50,7 @@ export const modelConfigs: Record<ModelType, z.infer<typeof modelConfigSchema>>
4350
cacheSupport: [],
4451
reasoningSupport: false,
4552
toolChoiceSupport: ['any', 'auto', 'tool'],
53+
pricing: { input: 0.003, output: 0.015, cacheRead: 0.0003, cacheWrite: 0.00375 },
4654
},
4755
'sonnet3.7': {
4856
name: 'Claude 3.7 Sonnet',
@@ -52,6 +60,7 @@ export const modelConfigs: Record<ModelType, z.infer<typeof modelConfigSchema>>
5260
cacheSupport: ['system', 'message', 'tool'],
5361
reasoningSupport: true,
5462
toolChoiceSupport: ['any', 'auto', 'tool'],
63+
pricing: { input: 0.003, output: 0.015, cacheRead: 0.0003, cacheWrite: 0.00375 },
5564
},
5665
'haiku3.5': {
5766
name: 'Claude 3.5 Haiku',
@@ -62,6 +71,7 @@ export const modelConfigs: Record<ModelType, z.infer<typeof modelConfigSchema>>
6271
reasoningSupport: false,
6372
toolChoiceSupport: ['any', 'auto', 'tool'],
6473
isHidden: true,
74+
pricing: { input: 0.0008, output: 0.004, cacheRead: 0.00008, cacheWrite: 0.001 },
6575
},
6676
'nova-pro': {
6777
name: 'Amazon Nova Pro',
@@ -71,6 +81,7 @@ export const modelConfigs: Record<ModelType, z.infer<typeof modelConfigSchema>>
7181
reasoningSupport: false,
7282
cacheSupport: ['system'],
7383
toolChoiceSupport: ['auto'],
84+
pricing: { input: 0.0008, output: 0.0032, cacheRead: 0.0002, cacheWrite: 0.0008 },
7485
},
7586
opus4: {
7687
name: 'Claude 4 Opus',
@@ -81,6 +92,7 @@ export const modelConfigs: Record<ModelType, z.infer<typeof modelConfigSchema>>
8192
reasoningSupport: true,
8293
toolChoiceSupport: ['any', 'auto', 'tool'],
8394
isHidden: true,
95+
pricing: { input: 0.015, output: 0.075, cacheRead: 0.0015, cacheWrite: 0.01875 },
8496
},
8597
'opus4.1': {
8698
name: 'Claude 4.1 Opus',
@@ -90,6 +102,7 @@ export const modelConfigs: Record<ModelType, z.infer<typeof modelConfigSchema>>
90102
cacheSupport: ['system', 'message', 'tool'],
91103
reasoningSupport: true,
92104
toolChoiceSupport: ['any', 'auto', 'tool'],
105+
pricing: { input: 0.015, output: 0.075, cacheRead: 0.0015, cacheWrite: 0.01875 },
93106
},
94107
sonnet4: {
95108
name: 'Claude 4 Sonnet',
@@ -99,5 +112,6 @@ export const modelConfigs: Record<ModelType, z.infer<typeof modelConfigSchema>>
99112
cacheSupport: ['system', 'message', 'tool'],
100113
reasoningSupport: true,
101114
toolChoiceSupport: ['any', 'auto', 'tool'],
115+
pricing: { input: 0.003, output: 0.015, cacheRead: 0.0003, cacheWrite: 0.00375 },
102116
},
103117
};

0 commit comments

Comments
 (0)