fix: use full OpenRouter model IDs in pricing lookup

MODEL_PRICING keys were 'gpt-oss-120b' but the actual model string
from env is 'openai/gpt-oss-120b'. The lookup always missed, so
calculateCost returned 0 for every call. Updated keys to match the
full OpenRouter model identifiers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
root
2026-03-10 18:54:36 +00:00
parent e465167651
commit b32e0f8489
3 changed files with 17 additions and 17 deletions
@@ -69,12 +69,12 @@ describe('llmConfig', () => {
describe('MODEL_PRICING', () => {
it('has free pricing for trinity models', () => {
const pricing = MODEL_PRICING['recursal/trinity-large'];
const pricing = MODEL_PRICING['arcee-ai/trinity-large-preview:free'];
expect(pricing).toEqual({ inputPerToken: 0, outputPerToken: 0 });
});
it('has correct pricing for gpt-oss-120b', () => {
const pricing = MODEL_PRICING['gpt-oss-120b'];
const pricing = MODEL_PRICING['openai/gpt-oss-120b'];
expect(pricing.inputPerToken).toBeCloseTo(0.000000039, 12);
expect(pricing.outputPerToken).toBeCloseTo(0.00000019, 12);
});
@@ -82,11 +82,11 @@ describe('MODEL_PRICING', () => {
describe('calculateCost', () => {
it('returns 0 for free model', () => {
expect(calculateCost('recursal/trinity-large', 1000, 500)).toBe(0);
expect(calculateCost('arcee-ai/trinity-large-preview:free', 1000, 500)).toBe(0);
});
it('calculates cost for paid model', () => {
const cost = calculateCost('gpt-oss-120b', 1000, 500);
const cost = calculateCost('openai/gpt-oss-120b', 1000, 500);
expect(cost).toBeCloseTo(0.000134, 8);
});
+2 -2
View File
@@ -29,8 +29,8 @@ export interface ModelPricing {
}
export const MODEL_PRICING: Record<string, ModelPricing> = {
'recursal/trinity-large': { inputPerToken: 0, outputPerToken: 0 },
'gpt-oss-120b': { inputPerToken: 0.039 / 1_000_000, outputPerToken: 0.19 / 1_000_000 },
'arcee-ai/trinity-large-preview:free': { inputPerToken: 0, outputPerToken: 0 },
'openai/gpt-oss-120b': { inputPerToken: 0.039 / 1_000_000, outputPerToken: 0.19 / 1_000_000 },
};
export function calculateCost(model: string, inputTokens: number, outputTokens: number): number {
@@ -50,7 +50,7 @@ describe('llmStatsService', () => {
it('records a successful call and retrieves stats', () => {
const service = setup();
service.record('socialNarration', 100, 50, 'gpt-oss-120b', 0, false, 42);
service.record('socialNarration', 100, 50, 'openai/gpt-oss-120b', 0, false, 42);
const stats = service.getStats();
expect(stats.types).toHaveLength(1);
@@ -64,8 +64,8 @@ describe('llmStatsService', () => {
it('tracks retries and failures', () => {
const service = setup();
service.record('socialNarration', 100, 50, 'gpt-oss-120b', 2, false, 1);
service.record('socialNarration', 100, 50, 'gpt-oss-120b', 0, true, 2);
service.record('socialNarration', 100, 50, 'openai/gpt-oss-120b', 2, false, 1);
service.record('socialNarration', 100, 50, 'openai/gpt-oss-120b', 0, true, 2);
const stats = service.getStats();
expect(stats.types[0].retryCount).toBe(1);
@@ -76,8 +76,8 @@ describe('llmStatsService', () => {
it('calculates min/max/avg cost', () => {
const service = setup();
service.record('backstoryAndDesires', 100, 50, 'recursal/trinity-large', 0, false, 1);
service.record('backstoryAndDesires', 1000, 500, 'gpt-oss-120b', 0, false, 2);
service.record('backstoryAndDesires', 100, 50, 'arcee-ai/trinity-large-preview:free', 0, false, 1);
service.record('backstoryAndDesires', 1000, 500, 'openai/gpt-oss-120b', 0, false, 2);
const stats = service.getStats();
const bs = stats.types.find(t => t.templateName === 'backstoryAndDesires')!;
@@ -91,7 +91,7 @@ describe('llmStatsService', () => {
it('respects tracking toggle', () => {
const service = setup();
service.setTrackingEnabled(false);
service.record('socialNarration', 100, 50, 'gpt-oss-120b', 0, false, 1);
service.record('socialNarration', 100, 50, 'openai/gpt-oss-120b', 0, false, 1);
const stats = service.getStats();
expect(stats.totalCount).toBe(0);
@@ -100,11 +100,11 @@ describe('llmStatsService', () => {
it('returns friendly labels', () => {
const service = setup();
service.record('backstoryAndDesires', 100, 50, 'recursal/trinity-large', 0, false, 1);
service.record('socialNarration', 100, 50, 'recursal/trinity-large', 0, false, 2);
service.record('batchedThoughts', 100, 50, 'recursal/trinity-large', 0, false, 3);
service.record('desireGeneration', 100, 50, 'recursal/trinity-large', 0, false, 4);
service.record('invention', 100, 50, 'recursal/trinity-large', 0, false, 5);
service.record('backstoryAndDesires', 100, 50, 'arcee-ai/trinity-large-preview:free', 0, false, 1);
service.record('socialNarration', 100, 50, 'arcee-ai/trinity-large-preview:free', 0, false, 2);
service.record('batchedThoughts', 100, 50, 'arcee-ai/trinity-large-preview:free', 0, false, 3);
service.record('desireGeneration', 100, 50, 'arcee-ai/trinity-large-preview:free', 0, false, 4);
service.record('invention', 100, 50, 'arcee-ai/trinity-large-preview:free', 0, false, 5);
const stats = service.getStats();
const labels = stats.types.map(t => t.label);