From b32e0f8489699cfd7b74833fa21c8e0a3e47ae2b Mon Sep 17 00:00:00 2001 From: root Date: Tue, 10 Mar 2026 18:54:36 +0000 Subject: [PATCH] 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 --- server/src/config/__tests__/llmConfig.test.ts | 8 +++---- server/src/config/llmConfig.ts | 4 ++-- .../src/llm/__tests__/llmStatsService.test.ts | 22 +++++++++---------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/server/src/config/__tests__/llmConfig.test.ts b/server/src/config/__tests__/llmConfig.test.ts index f249d06..d1dcf0b 100644 --- a/server/src/config/__tests__/llmConfig.test.ts +++ b/server/src/config/__tests__/llmConfig.test.ts @@ -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); }); diff --git a/server/src/config/llmConfig.ts b/server/src/config/llmConfig.ts index e33507f..61e9405 100644 --- a/server/src/config/llmConfig.ts +++ b/server/src/config/llmConfig.ts @@ -29,8 +29,8 @@ export interface ModelPricing { } export const MODEL_PRICING: Record = { - '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 { diff --git a/server/src/llm/__tests__/llmStatsService.test.ts b/server/src/llm/__tests__/llmStatsService.test.ts index 93e5565..4d09814 100644 --- a/server/src/llm/__tests__/llmStatsService.test.ts +++ b/server/src/llm/__tests__/llmStatsService.test.ts @@ -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);