From 098ca595d21a558f1253f2f1ef75e5f7014cfa02 Mon Sep 17 00:00:00 2001 From: Roo Code Date: Mon, 30 Jun 2025 08:21:33 +0000 Subject: [PATCH] Fixes #5144: Add comprehensive test coverage for OpenRouter upstream_inference_cost - Added test for BYOK cost calculation using upstream_inference_cost - Added test for graceful handling of missing upstream_inference_cost - Added test for reasoning tokens support - Verified cached token display functionality - All tests pass, confirming existing implementation correctly uses upstream_inference_cost --- .../providers/__tests__/openrouter.spec.ts | 143 ++++++++++++++++++ 1 file changed, 143 insertions(+) diff --git a/src/api/providers/__tests__/openrouter.spec.ts b/src/api/providers/__tests__/openrouter.spec.ts index 5c0e52c2c2..9891a8ba5f 100644 --- a/src/api/providers/__tests__/openrouter.spec.ts +++ b/src/api/providers/__tests__/openrouter.spec.ts @@ -181,6 +181,149 @@ describe("OpenRouterHandler", () => { ) }) + it("calculates cost using upstream_inference_cost for BYOK", async () => { + const handler = new OpenRouterHandler(mockOptions) + + const mockStream = { + async *[Symbol.asyncIterator]() { + yield { + id: "test-id", + choices: [{ delta: { content: "test response" } }], + } + yield { + id: "test-id", + choices: [{ delta: {} }], + usage: { + prompt_tokens: 100, + completion_tokens: 50, + cost: 0.002, // OpenRouter's cost + cost_details: { + upstream_inference_cost: 0.008, // Upstream provider cost + }, + prompt_tokens_details: { + cached_tokens: 20, // Cached tokens + }, + }, + } + }, + } + + const mockCreate = vitest.fn().mockResolvedValue(mockStream) + ;(OpenAI as any).prototype.chat = { + completions: { create: mockCreate }, + } as any + + const generator = handler.createMessage("test", []) + const chunks = [] + + for await (const chunk of generator) { + chunks.push(chunk) + } + + // Verify that totalCost includes both cost and upstream_inference_cost + expect(chunks).toHaveLength(2) + expect(chunks[1]).toEqual({ + type: "usage", + inputTokens: 100, + outputTokens: 50, + cacheReadTokens: 20, + totalCost: 0.01, // 0.002 + 0.008 + }) + }) + + it("handles missing upstream_inference_cost gracefully", async () => { + const handler = new OpenRouterHandler(mockOptions) + + const mockStream = { + async *[Symbol.asyncIterator]() { + yield { + id: "test-id", + choices: [{ delta: { content: "test response" } }], + } + yield { + id: "test-id", + choices: [{ delta: {} }], + usage: { + prompt_tokens: 100, + completion_tokens: 50, + cost: 0.005, // Only OpenRouter cost, no upstream cost + }, + } + }, + } + + const mockCreate = vitest.fn().mockResolvedValue(mockStream) + ;(OpenAI as any).prototype.chat = { + completions: { create: mockCreate }, + } as any + + const generator = handler.createMessage("test", []) + const chunks = [] + + for await (const chunk of generator) { + chunks.push(chunk) + } + + // Verify that totalCost falls back to just the cost field + expect(chunks).toHaveLength(2) + expect(chunks[1]).toEqual({ + type: "usage", + inputTokens: 100, + outputTokens: 50, + totalCost: 0.005, // Just the cost field + }) + }) + + it("includes reasoning tokens when present", async () => { + const handler = new OpenRouterHandler(mockOptions) + + const mockStream = { + async *[Symbol.asyncIterator]() { + yield { + id: "test-id", + choices: [{ delta: { content: "test response" } }], + } + yield { + id: "test-id", + choices: [{ delta: {} }], + usage: { + prompt_tokens: 100, + completion_tokens: 50, + completion_tokens_details: { + reasoning_tokens: 30, + }, + cost: 0.003, + cost_details: { + upstream_inference_cost: 0.007, + }, + }, + } + }, + } + + const mockCreate = vitest.fn().mockResolvedValue(mockStream) + ;(OpenAI as any).prototype.chat = { + completions: { create: mockCreate }, + } as any + + const generator = handler.createMessage("test", []) + const chunks = [] + + for await (const chunk of generator) { + chunks.push(chunk) + } + + // Verify reasoning tokens are included + expect(chunks).toHaveLength(2) + expect(chunks[1]).toEqual({ + type: "usage", + inputTokens: 100, + outputTokens: 50, + reasoningTokens: 30, + totalCost: 0.01, // 0.003 + 0.007 + }) + }) + it("supports the middle-out transform", async () => { const handler = new OpenRouterHandler({ ...mockOptions,