4316dbd3be
- 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
58 lines
2.0 KiB
TypeScript
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();
|
|
});
|
|
});
|