Files
flynn/src/models/costs.test.ts
T
William Valentin 4316dbd3be feat: add P2 features — retry policy, prompt templating, usage tracking, tech debt cleanup
- Extract shared splitMessage() into channels/utils.ts (dedup 4 adapters)
- Add Slack user name resolution with caching (users.info API)
- Add withRetry() with exponential backoff + jitter, isRetryable() filter
- Wire retry config into ModelRouter.chat() (non-streaming only)
- Add assembleSystemPrompt() multi-file template system (SOUL/AGENTS/IDENTITY/USER/TOOLS.md)
- Add usage tracking accumulators in NativeAgent + AgentOrchestrator
- Add estimateCost() with per-model pricing table
- Add /usage TUI command with full usage report formatting
- Add retrySchema and promptSchema to config schema

Tests: 569 passing, typecheck clean
2026-02-06 15:12:35 -08:00

58 lines
2.0 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import { estimateCost, MODEL_COSTS_PER_MILLION } from './costs.js';
describe('estimateCost', () => {
it('returns 0 for local/unknown models', () => {
expect(estimateCost(1000, 1000)).toBe(0);
expect(estimateCost(1000, 1000, 'some-local-model')).toBe(0);
});
it('uses default costs when model name is undefined', () => {
const cost = estimateCost(1_000_000, 1_000_000);
expect(cost).toBe(0);
});
it('calculates correctly for known Anthropic models', () => {
// claude-sonnet-4: $3/M input, $15/M output
const cost = estimateCost(1_000_000, 1_000_000, 'claude-sonnet-4-20250514');
expect(cost).toBe(3 + 15);
});
it('calculates correctly for claude-opus', () => {
// claude-opus-4: $15/M input, $75/M output
const cost = estimateCost(1_000_000, 500_000, 'claude-opus-4-20250514');
expect(cost).toBe(15 + 37.5);
});
it('calculates correctly for OpenAI models', () => {
// gpt-4o: $2.50/M input, $10/M output
const cost = estimateCost(2_000_000, 1_000_000, 'gpt-4o');
expect(cost).toBe(5 + 10);
});
it('handles small token counts', () => {
// 1000 tokens of claude-sonnet input: 1000 * 3 / 1_000_000 = 0.003
const cost = estimateCost(1000, 0, 'claude-sonnet-4-20250514');
expect(cost).toBeCloseTo(0.003);
});
it('handles zero tokens', () => {
const cost = estimateCost(0, 0, 'claude-sonnet-4-20250514');
expect(cost).toBe(0);
});
});
describe('MODEL_COSTS_PER_MILLION', () => {
it('has a default entry', () => {
expect(MODEL_COSTS_PER_MILLION['default']).toEqual({ input: 0, output: 0 });
});
it('has entries for all expected models', () => {
expect(MODEL_COSTS_PER_MILLION['claude-sonnet-4-20250514']).toBeDefined();
expect(MODEL_COSTS_PER_MILLION['claude-3-5-haiku-20241022']).toBeDefined();
expect(MODEL_COSTS_PER_MILLION['claude-opus-4-20250514']).toBeDefined();
expect(MODEL_COSTS_PER_MILLION['gpt-4o']).toBeDefined();
expect(MODEL_COSTS_PER_MILLION['gpt-4o-mini']).toBeDefined();
});
});