Skip to content

Commit 4b19010

Browse files
committed
Revert "Use OpenRouter's new usage flag for more reliable pricing responses"
This reverts commit a29689d.
1 parent 49803bf commit 4b19010

File tree

3 files changed

+68
-15
lines changed

3 files changed

+68
-15
lines changed

src/api/providers/cline.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export class ClineHandler implements ApiHandler {
2020

2121
async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {
2222
const model = this.getModel()
23-
yield* streamOpenRouterFormatRequest(
23+
const genId = yield* streamOpenRouterFormatRequest(
2424
this.client,
2525
systemPrompt,
2626
messages,
@@ -29,6 +29,27 @@ export class ClineHandler implements ApiHandler {
2929
this.options.thinkingBudgetTokens,
3030
this.options.openRouterProviderSorting,
3131
)
32+
33+
try {
34+
const response = await axios.get(`https://api.cline.bot/v1/generation?id=${genId}`, {
35+
headers: {
36+
Authorization: `Bearer ${this.options.clineApiKey}`,
37+
},
38+
timeout: 5_000, // this request hangs sometimes
39+
})
40+
41+
const generation = response.data
42+
console.log("cline generation details:", generation)
43+
yield {
44+
type: "usage",
45+
inputTokens: generation?.native_tokens_prompt || 0,
46+
outputTokens: generation?.native_tokens_completion || 0,
47+
totalCost: generation?.total_cost || 0,
48+
}
49+
} catch (error) {
50+
// ignore if fails
51+
console.error("Error fetching cline generation details:", error)
52+
}
3253
}
3354

3455
getModel(): { id: string; info: ModelInfo } {

src/api/providers/openrouter.ts

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export class OpenRouterHandler implements ApiHandler {
2929
@withRetry()
3030
async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {
3131
const model = this.getModel()
32-
yield* streamOpenRouterFormatRequest(
32+
const genId = yield* streamOpenRouterFormatRequest(
3333
this.client,
3434
systemPrompt,
3535
messages,
@@ -38,6 +38,45 @@ export class OpenRouterHandler implements ApiHandler {
3838
this.options.thinkingBudgetTokens,
3939
this.options.openRouterProviderSorting,
4040
)
41+
42+
if (genId) {
43+
await delay(500) // FIXME: necessary delay to ensure generation endpoint is ready
44+
try {
45+
const generationIterator = this.fetchGenerationDetails(genId)
46+
const generation = (await generationIterator.next()).value
47+
// console.log("OpenRouter generation details:", generation)
48+
yield {
49+
type: "usage",
50+
// cacheWriteTokens: 0,
51+
// cacheReadTokens: 0,
52+
// openrouter generation endpoint fails often
53+
inputTokens: generation?.native_tokens_prompt || 0,
54+
outputTokens: generation?.native_tokens_completion || 0,
55+
totalCost: generation?.total_cost || 0,
56+
}
57+
} catch (error) {
58+
// ignore if fails
59+
console.error("Error fetching OpenRouter generation details:", error)
60+
}
61+
}
62+
}
63+
64+
@withRetry({ maxRetries: 4, baseDelay: 250, maxDelay: 1000, retryAllErrors: true })
65+
async *fetchGenerationDetails(genId: string) {
66+
// console.log("Fetching generation details for:", genId)
67+
try {
68+
const response = await axios.get(`https://openrouter.ai/api/v1/generation?id=${genId}`, {
69+
headers: {
70+
Authorization: `Bearer ${this.options.openRouterApiKey}`,
71+
},
72+
timeout: 5_000, // this request hangs sometimes
73+
})
74+
yield response.data?.data
75+
} catch (error) {
76+
// ignore if fails
77+
console.error("Error fetching OpenRouter generation details:", error)
78+
throw error
79+
}
4180
}
4281

4382
getModel(): { id: string; info: ModelInfo } {

src/api/transform/openrouter-stream.ts

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export async function* streamOpenRouterFormatRequest(
1414
o3MiniReasoningEffort?: string,
1515
thinkingBudgetTokens?: number,
1616
openRouterProviderSorting?: string,
17-
): AsyncGenerator<ApiStreamChunk, undefined, unknown> {
17+
): AsyncGenerator<ApiStreamChunk, string | undefined, unknown> {
1818
// Convert Anthropic messages to OpenAI format
1919
let openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [
2020
{ role: "system", content: systemPrompt },
@@ -144,14 +144,12 @@ export async function* streamOpenRouterFormatRequest(
144144
stream: true,
145145
transforms: shouldApplyMiddleOutTransform ? ["middle-out"] : undefined,
146146
include_reasoning: true,
147-
stream_options: { include_usage: true },
148147
...(model.id === "openai/o3-mini" ? { reasoning_effort: o3MiniReasoningEffort || "medium" } : {}),
149148
...(reasoning ? { reasoning } : {}),
150149
...(openRouterProviderSorting ? { provider: { sort: openRouterProviderSorting } } : {}),
151150
})
152151

153-
// let genId: string | undefined
154-
let didOutputUsage: boolean = false
152+
let genId: string | undefined
155153

156154
for await (const chunk of stream) {
157155
// openrouter returns an error object instead of the openai sdk throwing an error
@@ -163,15 +161,8 @@ export async function* streamOpenRouterFormatRequest(
163161
throw new Error(`OpenRouter API Error ${error.code}: ${error.message}${metadataStr}`)
164162
}
165163

166-
if (chunk.usage && !didOutputUsage) {
167-
yield {
168-
type: "usage",
169-
inputTokens: chunk.usage.prompt_tokens || 0,
170-
outputTokens: chunk.usage.completion_tokens || 0,
171-
// @ts-ignore-next-line
172-
totalCost: chunk.usage.cost || 0,
173-
}
174-
didOutputUsage = true
164+
if (!genId && chunk.id) {
165+
genId = chunk.id
175166
}
176167

177168
const delta = chunk.choices[0]?.delta
@@ -191,4 +182,6 @@ export async function* streamOpenRouterFormatRequest(
191182
}
192183
}
193184
}
185+
186+
return genId
194187
}

0 commit comments

Comments
 (0)