Skip to content

Commit 0fd0800

Browse files
authored
Show openrouter key balance on the settings page (RooCodeInc#1990)
1 parent be8527c commit 0fd0800

File tree

3 files changed

+91
-2
lines changed

3 files changed

+91
-2
lines changed

.changeset/four-tools-pump.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"roo-cline": patch
3+
---
4+
5+
Show openrouter key balance on the settings screen

webview-ui/src/components/settings/ApiOptions.tsx

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ import {
4646
useOpenRouterModelProviders,
4747
OPENROUTER_DEFAULT_PROVIDER_NAME,
4848
} from "@/components/ui/hooks/useOpenRouterModelProviders"
49-
49+
import { useOpenRouterKeyInfo } from "@/components/ui/hooks/useOpenRouterKeyInfo"
5050
import { MODELS_BY_PROVIDER, PROVIDERS, AWS_REGIONS, VERTEX_REGIONS } from "./constants"
5151
import { VSCodeButtonLink } from "../common/VSCodeButtonLink"
5252
import { ModelInfoView } from "./ModelInfoView"
@@ -57,6 +57,23 @@ import { ApiErrorMessage } from "./ApiErrorMessage"
5757
import { ThinkingBudget } from "./ThinkingBudget"
5858
import { R1FormatSetting } from "./R1FormatSetting"
5959

60+
// Component to display OpenRouter API key balance
61+
const OpenRouterBalanceDisplay = ({ apiKey, baseUrl }: { apiKey: string; baseUrl?: string }) => {
62+
const { data: keyInfo } = useOpenRouterKeyInfo(apiKey, baseUrl)
63+
64+
if (!keyInfo || !keyInfo.limit) {
65+
return null
66+
}
67+
68+
const formattedBalance = (keyInfo.limit - keyInfo.usage).toFixed(2)
69+
70+
return (
71+
<VSCodeLink href="https://openrouter.ai/settings/keys" className="text-vscode-foreground hover:underline">
72+
${formattedBalance}
73+
</VSCodeLink>
74+
)
75+
}
76+
6077
interface ApiOptionsProps {
6178
uriScheme: string | undefined
6279
apiConfiguration: ApiConfiguration
@@ -274,7 +291,15 @@ const ApiOptions = ({
274291
onInput={handleInputChange("openRouterApiKey")}
275292
placeholder={t("settings:placeholders.apiKey")}
276293
className="w-full">
277-
<label className="block font-medium mb-1">{t("settings:providers.openRouterApiKey")}</label>
294+
<div className="flex justify-between items-center mb-1">
295+
<label className="block font-medium">{t("settings:providers.openRouterApiKey")}</label>
296+
{apiConfiguration?.openRouterApiKey && (
297+
<OpenRouterBalanceDisplay
298+
apiKey={apiConfiguration.openRouterApiKey}
299+
baseUrl={apiConfiguration.openRouterBaseUrl}
300+
/>
301+
)}
302+
</div>
278303
</VSCodeTextField>
279304
<div className="text-sm text-vscode-descriptionForeground -mt-2">
280305
{t("settings:providers.apiKeyStorageNotice")}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import axios from "axios"
2+
import { z } from "zod"
3+
import { useQuery, UseQueryOptions } from "@tanstack/react-query"
4+
5+
// Define schema for OpenRouter key response
6+
const openRouterKeyInfoSchema = z.object({
7+
data: z.object({
8+
label: z.string(),
9+
usage: z.number(),
10+
is_free_tier: z.boolean(),
11+
is_provisioning_key: z.boolean(),
12+
rate_limit: z.object({
13+
requests: z.number(),
14+
interval: z.string(),
15+
}),
16+
limit: z.number().nullable(),
17+
}),
18+
})
19+
20+
export type OpenRouterKeyInfo = z.infer<typeof openRouterKeyInfoSchema>["data"]
21+
22+
async function getOpenRouterKeyInfo(apiKey?: string, baseUrl?: string) {
23+
if (!apiKey) return null
24+
25+
try {
26+
// Use the provided base URL or default to OpenRouter's API URL
27+
const apiBaseUrl = baseUrl || "https://openrouter.ai/api/v1"
28+
29+
const keyEndpoint = `${apiBaseUrl}/key`
30+
31+
const response = await axios.get(keyEndpoint, {
32+
headers: {
33+
Authorization: `Bearer ${apiKey}`,
34+
},
35+
})
36+
37+
const result = openRouterKeyInfoSchema.safeParse(response.data)
38+
if (!result.success) {
39+
console.error("OpenRouter API key info validation failed:", result.error)
40+
return null
41+
}
42+
43+
return result.data.data
44+
} catch (error) {
45+
console.error("Error fetching OpenRouter key info:", error)
46+
return null
47+
}
48+
}
49+
50+
type UseOpenRouterKeyInfoOptions = Omit<UseQueryOptions<OpenRouterKeyInfo | null>, "queryKey" | "queryFn">
51+
export const useOpenRouterKeyInfo = (apiKey?: string, baseUrl?: string, options?: UseOpenRouterKeyInfoOptions) => {
52+
return useQuery<OpenRouterKeyInfo | null>({
53+
queryKey: ["openrouter-key-info", apiKey, baseUrl],
54+
queryFn: () => getOpenRouterKeyInfo(apiKey, baseUrl),
55+
staleTime: 30 * 1000, // 30 seconds
56+
enabled: !!apiKey,
57+
...options,
58+
})
59+
}

0 commit comments

Comments
 (0)