Show openrouter key balance on the settings page (#1990)

This commit is contained in:
Matt Rubens 2025-03-25 22:41:21 -04:00 committed by GitHub
parent be8527c995
commit 0fd0800ba9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 91 additions and 2 deletions

View file

@ -0,0 +1,5 @@
---
"roo-cline": patch
---
Show openrouter key balance on the settings screen

View file

@ -46,7 +46,7 @@ import {
useOpenRouterModelProviders,
OPENROUTER_DEFAULT_PROVIDER_NAME,
} from "@/components/ui/hooks/useOpenRouterModelProviders"
import { useOpenRouterKeyInfo } from "@/components/ui/hooks/useOpenRouterKeyInfo"
import { MODELS_BY_PROVIDER, PROVIDERS, AWS_REGIONS, VERTEX_REGIONS } from "./constants"
import { VSCodeButtonLink } from "../common/VSCodeButtonLink"
import { ModelInfoView } from "./ModelInfoView"
@ -57,6 +57,23 @@ import { ApiErrorMessage } from "./ApiErrorMessage"
import { ThinkingBudget } from "./ThinkingBudget"
import { R1FormatSetting } from "./R1FormatSetting"
// Component to display OpenRouter API key balance
const OpenRouterBalanceDisplay = ({ apiKey, baseUrl }: { apiKey: string; baseUrl?: string }) => {
const { data: keyInfo } = useOpenRouterKeyInfo(apiKey, baseUrl)
if (!keyInfo || !keyInfo.limit) {
return null
}
const formattedBalance = (keyInfo.limit - keyInfo.usage).toFixed(2)
return (
<VSCodeLink href="https://openrouter.ai/settings/keys" className="text-vscode-foreground hover:underline">
${formattedBalance}
</VSCodeLink>
)
}
interface ApiOptionsProps {
uriScheme: string | undefined
apiConfiguration: ApiConfiguration
@ -274,7 +291,15 @@ const ApiOptions = ({
onInput={handleInputChange("openRouterApiKey")}
placeholder={t("settings:placeholders.apiKey")}
className="w-full">
<label className="block font-medium mb-1">{t("settings:providers.openRouterApiKey")}</label>
<div className="flex justify-between items-center mb-1">
<label className="block font-medium">{t("settings:providers.openRouterApiKey")}</label>
{apiConfiguration?.openRouterApiKey && (
<OpenRouterBalanceDisplay
apiKey={apiConfiguration.openRouterApiKey}
baseUrl={apiConfiguration.openRouterBaseUrl}
/>
)}
</div>
</VSCodeTextField>
<div className="text-sm text-vscode-descriptionForeground -mt-2">
{t("settings:providers.apiKeyStorageNotice")}

View file

@ -0,0 +1,59 @@
import axios from "axios"
import { z } from "zod"
import { useQuery, UseQueryOptions } from "@tanstack/react-query"
// Define schema for OpenRouter key response
const openRouterKeyInfoSchema = z.object({
data: z.object({
label: z.string(),
usage: z.number(),
is_free_tier: z.boolean(),
is_provisioning_key: z.boolean(),
rate_limit: z.object({
requests: z.number(),
interval: z.string(),
}),
limit: z.number().nullable(),
}),
})
export type OpenRouterKeyInfo = z.infer<typeof openRouterKeyInfoSchema>["data"]
async function getOpenRouterKeyInfo(apiKey?: string, baseUrl?: string) {
if (!apiKey) return null
try {
// Use the provided base URL or default to OpenRouter's API URL
const apiBaseUrl = baseUrl || "https://openrouter.ai/api/v1"
const keyEndpoint = `${apiBaseUrl}/key`
const response = await axios.get(keyEndpoint, {
headers: {
Authorization: `Bearer ${apiKey}`,
},
})
const result = openRouterKeyInfoSchema.safeParse(response.data)
if (!result.success) {
console.error("OpenRouter API key info validation failed:", result.error)
return null
}
return result.data.data
} catch (error) {
console.error("Error fetching OpenRouter key info:", error)
return null
}
}
type UseOpenRouterKeyInfoOptions = Omit<UseQueryOptions<OpenRouterKeyInfo | null>, "queryKey" | "queryFn">
export const useOpenRouterKeyInfo = (apiKey?: string, baseUrl?: string, options?: UseOpenRouterKeyInfoOptions) => {
return useQuery<OpenRouterKeyInfo | null>({
queryKey: ["openrouter-key-info", apiKey, baseUrl],
queryFn: () => getOpenRouterKeyInfo(apiKey, baseUrl),
staleTime: 30 * 1000, // 30 seconds
enabled: !!apiKey,
...options,
})
}