diff --git a/src/lib/utils/providers.ts b/src/lib/utils/providers.ts new file mode 100644 index 0000000..662043f --- /dev/null +++ b/src/lib/utils/providers.ts @@ -0,0 +1,34 @@ +import { Result, ResultAsync } from 'neverthrow'; + +export type OpenRouterApiKeyData = { + label: string; + usage: number; + is_free_tier: boolean; + is_provisioning_key: boolean; + limit: number; + limit_remaining: number; +}; + +export const OpenRouter = { + getApiKey: async (key: string): Promise> => { + return await ResultAsync.fromPromise( + (async () => { + const res = await fetch('https://openrouter.ai/api/v1/key', { + headers: { + Authorization: `Bearer ${key}`, + 'Content-Type': 'application/json', + }, + }); + + if (!res.ok) throw new Error('Failed to get API key'); + + const { data } = await res.json(); + + if (!data) throw new Error('No info returned for api key'); + + return data as OpenRouterApiKeyData; + })(), + (e) => `Failed to get API key ${e}` + ); + }, +}; diff --git a/src/routes/account/+layout.svelte b/src/routes/account/+layout.svelte index ddceb26..2cb0b4e 100644 --- a/src/routes/account/+layout.svelte +++ b/src/routes/account/+layout.svelte @@ -52,6 +52,10 @@ name: 'New Chat', keys: [cmdOrCtrl, 'Shift', 'O'], }, + { + name: 'Search Messages', + keys: [cmdOrCtrl, 'K'], + }, ]; async function signOut() { diff --git a/src/routes/account/api-keys/provider-card.svelte b/src/routes/account/api-keys/provider-card.svelte index a6242a9..1322f72 100644 --- a/src/routes/account/api-keys/provider-card.svelte +++ b/src/routes/account/api-keys/provider-card.svelte @@ -7,10 +7,12 @@ import { Input } from '$lib/components/ui/input'; import { Link } from '$lib/components/ui/link'; import { session } from '$lib/state/session.svelte.js'; - import type { Provider, ProviderMeta } from '$lib/types'; + import { Provider, type ProviderMeta } from '$lib/types'; import KeyIcon from '~icons/lucide/key'; import { useConvexClient } from 'convex-svelte'; import { ResultAsync } from 'neverthrow'; + import { resource } from 'runed'; + import * as providers from '$lib/utils/providers'; type Props = { provider: Provider; @@ -57,6 +59,19 @@ loading = false; } + + const apiKeyInfoResource = resource( + () => keyQuery.data, + async (key) => { + if (!key) return null; + + if (provider === Provider.OpenRouter) { + return (await providers.OpenRouter.getApiKey(key)).unwrapOr(null); + } + + return null; + } + ); @@ -80,12 +95,30 @@ value={keyQuery.data!} /> {/if} - - Get your API key from - - {meta.title} - - + {#if keyQuery.data} + {#if apiKeyInfoResource.loading} +
+ {:else if apiKeyInfoResource.current} + + ${apiKeyInfoResource.current?.usage.toFixed(3)} used ・ ${apiKeyInfoResource.current?.limit_remaining.toFixed( + 3 + )} remaining + + {:else} + + We encountered an error while trying to check your usage. Please try again later. + + {/if} + {:else} + + Get your API key from + + {meta.title} + + + {/if}