mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-05 08:14:30 +00:00
feat(pricing): register Claude Sonnet 5 in the pricing registry
Add explicit `claude-sonnet-5` and `claude-sonnet-5-thinking` entries to the
web-server PRICING_REGISTRY at the standard Sonnet rates ($3/$15 in/out,
$3.75/$0.30 cache).
The model catalog and dashboard already ship Sonnet 5 (it is the Claude
default), but pricing was missing: getModelPricing() fell through to
UNKNOWN_MODEL_PRICING, which only coincidentally matches Sonnet rates. As a
result hasCustomPricing('claude-sonnet-5') returned false and getKnownModels()
omitted it, so Sonnet 5 was treated as an unknown model in cost accounting and
known-model listings.
Registering it explicitly makes the pricing intentional and consistent with
every other Claude model, without changing any resolved values.
Built [OnSteroids](https://onsteroids.ai)
This commit is contained in:
1 parent
f1eee3029e
commit
1e63c37baf
2 files changed
+49
No files matched your search
@@ -216,6 +216,21 @@ const PRICING_REGISTRY: Record<string, ModelPricing> = {
|
||||
cacheCreationPerMillion: 3.75,
|
||||
cacheReadPerMillion: 0.3,
|
||||
},
|
||||
// Claude Sonnet 5 ($3/$15) — latest Sonnet; shares the standard Sonnet rates.
|
||||
// Registered explicitly so it is a known model rather than relying on the
|
||||
// unknown-model fallback that coincidentally matches these rates.
|
||||
'claude-sonnet-5': {
|
||||
inputPerMillion: 3.0,
|
||||
outputPerMillion: 15.0,
|
||||
cacheCreationPerMillion: 3.75,
|
||||
cacheReadPerMillion: 0.3,
|
||||
},
|
||||
'claude-sonnet-5-thinking': {
|
||||
inputPerMillion: 3.0,
|
||||
outputPerMillion: 15.0,
|
||||
cacheCreationPerMillion: 3.75,
|
||||
cacheReadPerMillion: 0.3,
|
||||
},
|
||||
// Claude 4 Opus ($15/$75)
|
||||
'claude-4-opus-20250514': {
|
||||
inputPerMillion: 15.0,
|
||||
|
||||
@@ -277,6 +277,14 @@ describe('model-pricing', () => {
|
||||
expect(fable5.cacheReadPerMillion).toBe(1.0);
|
||||
});
|
||||
|
||||
it('should return correct pricing for Claude Sonnet 5', () => {
|
||||
const sonnet5 = getModelPricing('claude-sonnet-5');
|
||||
expect(sonnet5.inputPerMillion).toBe(3.0);
|
||||
expect(sonnet5.outputPerMillion).toBe(15.0);
|
||||
expect(sonnet5.cacheCreationPerMillion).toBe(3.75);
|
||||
expect(sonnet5.cacheReadPerMillion).toBe(0.3);
|
||||
});
|
||||
|
||||
it('should not map unknown future model families onto known family pricing', () => {
|
||||
const fallback = getModelPricing('unknown-model-xyz');
|
||||
|
||||
@@ -398,6 +406,17 @@ describe('model-pricing', () => {
|
||||
expect(cost).toBe(73.5); // 10 + 50 + 12.5 + 1.0
|
||||
});
|
||||
|
||||
it('should calculate Claude Sonnet 5 cost including cache token rates', () => {
|
||||
const usage: TokenUsage = {
|
||||
inputTokens: 1_000_000,
|
||||
outputTokens: 1_000_000,
|
||||
cacheCreationTokens: 1_000_000,
|
||||
cacheReadTokens: 1_000_000,
|
||||
};
|
||||
const cost = calculateCost(usage, 'claude-sonnet-5');
|
||||
expect(cost).toBe(22.05); // 3 + 15 + 3.75 + 0.3
|
||||
});
|
||||
|
||||
it('should calculate fast-tier Claude Opus 4.8 cost (2x premium)', () => {
|
||||
const usage: TokenUsage = {
|
||||
inputTokens: 1_000_000,
|
||||
@@ -426,6 +445,13 @@ describe('model-pricing', () => {
|
||||
const models = getKnownModels();
|
||||
expect(models.some((m) => m.startsWith('glm-'))).toBe(true);
|
||||
});
|
||||
|
||||
it('should include Claude Sonnet 5 as an explicitly registered model', () => {
|
||||
// Sonnet 5 must be a first-class known model rather than relying on the
|
||||
// unknown-model fallback that happens to share Sonnet's $3/$15 rates.
|
||||
const models = getKnownModels();
|
||||
expect(models).toContain('claude-sonnet-5');
|
||||
});
|
||||
});
|
||||
|
||||
describe('hasCustomPricing', () => {
|
||||
@@ -434,6 +460,14 @@ describe('model-pricing', () => {
|
||||
expect(hasCustomPricing('glm-4.6')).toBe(true);
|
||||
});
|
||||
|
||||
it('should treat Claude Sonnet 5 as a known model with explicit pricing', () => {
|
||||
// Without an explicit registry entry Sonnet 5 falls through to
|
||||
// UNKNOWN_MODEL_PRICING (which coincidentally matches Sonnet rates);
|
||||
// register it so cost accounting is intentional, not accidental.
|
||||
expect(hasCustomPricing('claude-sonnet-5')).toBe(true);
|
||||
expect(hasCustomPricing('claude-sonnet-5-thinking')).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true for deterministic qwen3-coder alias', () => {
|
||||
expect(hasCustomPricing('qwen3-coder')).toBe(true);
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user