/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ /** * ModelSelector — dropdown to pick the LLM model. * Free models available to everyone via server proxy. * BYOK models selectable always — greyed out with lock icon when key is missing, * fully styled when key is configured. Selecting a locked model triggers the * inline key prompt in ChatPanel. */ import { useCallback, useEffect, useState } from 'react'; import { Check, Key } from 'lucide-react'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { useViewerStore } from '@/store'; import { FREE_MODELS, getModelById, getByokModelsForSource } from '@/lib/llm/models'; import type { LLMModel } from '@/lib/llm/types'; import { hasAnthropicKey, hasOpenaiKey, subscribeApiKeys } from '@/services/api-keys'; function formatContextWindow(tokens: number): string { if (tokens >= 1_000_000) return `${(tokens / 1_000_000).toFixed(0)}M`; return `${(tokens / 1_000).toFixed(0)}K`; } function CostBadge({ cost }: { cost?: LLMModel['cost'] }) { if (!cost) return null; const color = cost === '$$$' ? 'text-amber-500' : cost === '$$' ? 'text-blue-500' : 'text-emerald-500'; return {cost}; } export function ModelSelector() { const activeModel = useViewerStore((s) => s.chatActiveModel); const setActiveModel = useViewerStore((s) => s.setChatActiveModel); const [hasAnthropic, setHasAnthropic] = useState(hasAnthropicKey); const [hasOpenai, setHasOpenai] = useState(hasOpenaiKey); useEffect(() => { const refresh = () => { setHasAnthropic(hasAnthropicKey()); setHasOpenai(hasOpenaiKey()); }; return subscribeApiKeys(refresh); }, []); const handleChange = useCallback((value: string) => { setActiveModel(value); }, [setActiveModel]); const current = getModelById(activeModel); const anthropicModels = getByokModelsForSource('anthropic'); const openaiModels = getByokModelsForSource('openai'); return ( ); }