From 0fd0800ba9ccb911e3754faccf3977793e970038 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Tue, 25 Mar 2025 22:41:21 -0400 Subject: [PATCH] Show openrouter key balance on the settings page (#1990) --- .changeset/four-tools-pump.md | 5 ++ .../src/components/settings/ApiOptions.tsx | 29 ++++++++- .../ui/hooks/useOpenRouterKeyInfo.ts | 59 +++++++++++++++++++ 3 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 .changeset/four-tools-pump.md create mode 100644 webview-ui/src/components/ui/hooks/useOpenRouterKeyInfo.ts diff --git a/.changeset/four-tools-pump.md b/.changeset/four-tools-pump.md new file mode 100644 index 0000000000..6d5e50f35f --- /dev/null +++ b/.changeset/four-tools-pump.md @@ -0,0 +1,5 @@ +--- +"roo-cline": patch +--- + +Show openrouter key balance on the settings screen diff --git a/webview-ui/src/components/settings/ApiOptions.tsx b/webview-ui/src/components/settings/ApiOptions.tsx index 7cbd00f0a4..15d22a7b6f 100644 --- a/webview-ui/src/components/settings/ApiOptions.tsx +++ b/webview-ui/src/components/settings/ApiOptions.tsx @@ -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 ( + + ${formattedBalance} + + ) +} + interface ApiOptionsProps { uriScheme: string | undefined apiConfiguration: ApiConfiguration @@ -274,7 +291,15 @@ const ApiOptions = ({ onInput={handleInputChange("openRouterApiKey")} placeholder={t("settings:placeholders.apiKey")} className="w-full"> - +
+ + {apiConfiguration?.openRouterApiKey && ( + + )} +
{t("settings:providers.apiKeyStorageNotice")} diff --git a/webview-ui/src/components/ui/hooks/useOpenRouterKeyInfo.ts b/webview-ui/src/components/ui/hooks/useOpenRouterKeyInfo.ts new file mode 100644 index 0000000000..44b7aaade5 --- /dev/null +++ b/webview-ui/src/components/ui/hooks/useOpenRouterKeyInfo.ts @@ -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["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, "queryKey" | "queryFn"> +export const useOpenRouterKeyInfo = (apiKey?: string, baseUrl?: string, options?: UseOpenRouterKeyInfoOptions) => { + return useQuery({ + queryKey: ["openrouter-key-info", apiKey, baseUrl], + queryFn: () => getOpenRouterKeyInfo(apiKey, baseUrl), + staleTime: 30 * 1000, // 30 seconds + enabled: !!apiKey, + ...options, + }) +}