Skip to content

Commit c51f59e

Browse files
committed
Requesty: Correctly calculate request costs
1 parent e14b1b2 commit c51f59e

File tree

3 files changed

+47
-17
lines changed

3 files changed

+47
-17
lines changed

src/api/providers/__tests__/requesty.test.ts

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@ describe("RequestyHandler", () => {
2222
contextWindow: 4000,
2323
supportsPromptCache: false,
2424
supportsImages: true,
25-
inputPrice: 0,
26-
outputPrice: 0,
25+
inputPrice: 1,
26+
outputPrice: 10,
27+
cacheReadsPrice: 0.1,
28+
cacheWritesPrice: 1.5,
2729
},
2830
openAiStreamingEnabled: true,
2931
includeMaxTokens: true, // Add this to match the implementation
@@ -83,8 +85,12 @@ describe("RequestyHandler", () => {
8385
yield {
8486
choices: [{ delta: { content: " world" } }],
8587
usage: {
86-
prompt_tokens: 10,
87-
completion_tokens: 5,
88+
prompt_tokens: 30,
89+
completion_tokens: 10,
90+
prompt_tokens_details: {
91+
cached_tokens: 15,
92+
caching_tokens: 5,
93+
},
8894
},
8995
}
9096
},
@@ -105,10 +111,11 @@ describe("RequestyHandler", () => {
105111
{ type: "text", text: " world" },
106112
{
107113
type: "usage",
108-
inputTokens: 10,
109-
outputTokens: 5,
110-
cacheWriteTokens: undefined,
111-
cacheReadTokens: undefined,
114+
inputTokens: 30,
115+
outputTokens: 10,
116+
cacheWriteTokens: 5,
117+
cacheReadTokens: 15,
118+
totalCost: 0.000119, // (10 * 1 / 1,000,000) + (5 * 1.5 / 1,000,000) + (15 * 0.1 / 1,000,000) + (10 * 10 / 1,000,000)
112119
},
113120
])
114121

@@ -182,6 +189,9 @@ describe("RequestyHandler", () => {
182189
type: "usage",
183190
inputTokens: 10,
184191
outputTokens: 5,
192+
cacheWriteTokens: 0,
193+
cacheReadTokens: 0,
194+
totalCost: 0.00006, // (10 * 1 / 1,000,000) + (5 * 10 / 1,000,000)
185195
},
186196
])
187197

src/api/providers/openai.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl
111111
}
112112
}
113113
if (chunk.usage) {
114-
yield this.processUsageMetrics(chunk.usage)
114+
yield this.processUsageMetrics(chunk.usage, modelInfo)
115115
}
116116
}
117117
} else {
@@ -134,11 +134,11 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl
134134
type: "text",
135135
text: response.choices[0]?.message.content || "",
136136
}
137-
yield this.processUsageMetrics(response.usage)
137+
yield this.processUsageMetrics(response.usage, modelInfo)
138138
}
139139
}
140140

141-
protected processUsageMetrics(usage: any): ApiStreamUsageChunk {
141+
protected processUsageMetrics(usage: any, modelInfo?: ModelInfo): ApiStreamUsageChunk {
142142
return {
143143
type: "usage",
144144
inputTokens: usage?.prompt_tokens || 0,

src/api/providers/requesty.ts

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,20 @@
11
import axios from "axios"
22

33
import { ModelInfo, requestyModelInfoSaneDefaults, requestyDefaultModelId } from "../../shared/api"
4-
import { parseApiPrice } from "../../utils/cost"
4+
import { calculateApiCostOpenAI, parseApiPrice } from "../../utils/cost"
55
import { ApiStreamUsageChunk } from "../transform/stream"
66
import { OpenAiHandler, OpenAiHandlerOptions } from "./openai"
7+
import OpenAI from "openai"
8+
9+
// Requesty usage includes an extra field for Anthropic use cases.
10+
// Safely cast the prompt token details section to the appropriate structure.
11+
interface RequestyUsage extends OpenAI.CompletionUsage {
12+
prompt_tokens_details?: {
13+
caching_tokens?: number
14+
cached_tokens?: number
15+
}
16+
total_cost?: number
17+
}
718

819
export class RequestyHandler extends OpenAiHandler {
920
constructor(options: OpenAiHandlerOptions) {
@@ -27,13 +38,22 @@ export class RequestyHandler extends OpenAiHandler {
2738
}
2839
}
2940

30-
protected override processUsageMetrics(usage: any): ApiStreamUsageChunk {
41+
protected override processUsageMetrics(usage: any, modelInfo?: ModelInfo): ApiStreamUsageChunk {
42+
const requestyUsage = usage as RequestyUsage
43+
const inputTokens = requestyUsage?.prompt_tokens || 0
44+
const outputTokens = requestyUsage?.completion_tokens || 0
45+
const cacheWriteTokens = requestyUsage?.prompt_tokens_details?.caching_tokens || 0
46+
const cacheReadTokens = requestyUsage?.prompt_tokens_details?.cached_tokens || 0
47+
const totalCost = modelInfo
48+
? calculateApiCostOpenAI(modelInfo, inputTokens, outputTokens, cacheWriteTokens, cacheReadTokens)
49+
: 0
3150
return {
3251
type: "usage",
33-
inputTokens: usage?.prompt_tokens || 0,
34-
outputTokens: usage?.completion_tokens || 0,
35-
cacheWriteTokens: usage?.cache_creation_input_tokens,
36-
cacheReadTokens: usage?.cache_read_input_tokens,
52+
inputTokens: inputTokens,
53+
outputTokens: outputTokens,
54+
cacheWriteTokens: cacheWriteTokens,
55+
cacheReadTokens: cacheReadTokens,
56+
totalCost: totalCost,
3757
}
3858
}
3959
}

0 commit comments

Comments
 (0)