From 31644dbcd8566aaa3a2f729902360d899a41dae6 Mon Sep 17 00:00:00 2001 From: xunc lee <490842678@qq.com> Date: Fri, 26 Dec 2025 11:19:59 +0800 Subject: [PATCH] feat: add toggle to show unvalidated models in model selector (#413) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: add toggle to show unvalidated models in model selector Add a toggle switch in the model configuration dialog to allow users to display models that haven't been validated. This helps users who work with model providers that have disabled their verification endpoints. Changes: - Add showUnvalidatedModels field to MultiModelConfig type - Add setShowUnvalidatedModels method to useModelConfig hook - Add Switch toggle in model-config-dialog footer - Update model-selector to filter based on showUnvalidatedModels setting - Add warning icon for unvalidated models in the selector - Add i18n translations for en/zh/ja Closes #410 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 * fix: wrap AlertTriangle in span for title attribute The AlertTriangle icon from lucide-react doesn't support the title prop directly. Wrapped it in a span element to properly display the tooltip. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --------- Co-authored-by: Claude Opus 4.5 --- components/chat-input.tsx | 3 +++ components/chat-panel.tsx | 1 + components/model-config-dialog.tsx | 22 ++++++++++++--- components/model-selector.tsx | 43 +++++++++++++++++++++++------- hooks/use-model-config.ts | 11 ++++++++ lib/i18n/dictionaries/en.json | 5 +++- lib/i18n/dictionaries/ja.json | 5 +++- lib/i18n/dictionaries/zh.json | 5 +++- lib/types/model-config.ts | 1 + 9 files changed, 79 insertions(+), 17 deletions(-) diff --git a/components/chat-input.tsx b/components/chat-input.tsx index fd67c84..774b6f2 100644 --- a/components/chat-input.tsx +++ b/components/chat-input.tsx @@ -162,6 +162,7 @@ interface ChatInputProps { models?: FlattenedModel[] selectedModelId?: string onModelSelect?: (modelId: string | undefined) => void + showUnvalidatedModels?: boolean onConfigureModels?: () => void } @@ -183,6 +184,7 @@ export function ChatInput({ models = [], selectedModelId, onModelSelect = () => {}, + showUnvalidatedModels = false, onConfigureModels = () => {}, }: ChatInputProps) { const dict = useDictionary() @@ -482,6 +484,7 @@ export function ChatInput({ onSelect={onModelSelect} onConfigure={onConfigureModels} disabled={isDisabled} + showUnvalidatedModels={showUnvalidatedModels} />
diff --git a/components/chat-panel.tsx b/components/chat-panel.tsx index ef7e8ef..086037e 100644 --- a/components/chat-panel.tsx +++ b/components/chat-panel.tsx @@ -1071,6 +1071,7 @@ export default function ChatPanel({ models={modelConfig.models} selectedModelId={modelConfig.selectedModelId} onModelSelect={modelConfig.setSelectedModelId} + showUnvalidatedModels={modelConfig.showUnvalidatedModels} onConfigureModels={() => setShowModelConfigDialog(true)} /> diff --git a/components/model-config-dialog.tsx b/components/model-config-dialog.tsx index 7c4534d..88ade22 100644 --- a/components/model-config-dialog.tsx +++ b/components/model-config-dialog.tsx @@ -50,6 +50,7 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select" +import { Switch } from "@/components/ui/switch" import { useDictionary } from "@/hooks/use-dictionary" import type { UseModelConfigReturn } from "@/hooks/use-model-config" import { formatMessage } from "@/lib/i18n/utils" @@ -1447,10 +1448,23 @@ export function ModelConfigDialog({ {/* Footer */}
-

- - {dict.modelConfig.apiKeyStored} -

+
+
+ + +
+

+ + {dict.modelConfig.apiKeyStored} +

+
diff --git a/components/model-selector.tsx b/components/model-selector.tsx index 8153433..237474e 100644 --- a/components/model-selector.tsx +++ b/components/model-selector.tsx @@ -1,6 +1,13 @@ "use client" -import { Bot, Check, ChevronDown, Server, Settings2 } from "lucide-react" +import { + AlertTriangle, + Bot, + Check, + ChevronDown, + Server, + Settings2, +} from "lucide-react" import { useMemo, useState } from "react" import { ModelSelectorContent, @@ -26,6 +33,7 @@ interface ModelSelectorProps { onSelect: (modelId: string | undefined) => void onConfigure: () => void disabled?: boolean + showUnvalidatedModels?: boolean } // Map our provider names to models.dev logo names @@ -67,17 +75,20 @@ export function ModelSelector({ onSelect, onConfigure, disabled = false, + showUnvalidatedModels = false, }: ModelSelectorProps) { const dict = useDictionary() const [open, setOpen] = useState(false) - // Only show validated models in the selector - const validatedModels = useMemo( - () => models.filter((m) => m.validated === true), - [models], - ) + // Filter models based on showUnvalidatedModels setting + const displayModels = useMemo(() => { + if (showUnvalidatedModels) { + return models + } + return models.filter((m) => m.validated === true) + }, [models, showUnvalidatedModels]) const groupedModels = useMemo( - () => groupModelsByProvider(validatedModels), - [validatedModels], + () => groupModelsByProvider(displayModels), + [displayModels], ) // Find selected model for display @@ -126,7 +137,7 @@ export function ModelSelector({ /> - {validatedModels.length === 0 && models.length > 0 + {displayModels.length === 0 && models.length > 0 ? dict.modelConfig.noVerifiedModels : dict.modelConfig.noModelsFound} @@ -191,6 +202,16 @@ export function ModelSelector({ {model.modelId} + {model.validated !== true && ( + + + + )} ))} @@ -213,7 +234,9 @@ export function ModelSelector({ {/* Info text */}
- {dict.modelConfig.onlyVerifiedShown} + {showUnvalidatedModels + ? dict.modelConfig.allModelsShown + : dict.modelConfig.onlyVerifiedShown}
diff --git a/hooks/use-model-config.ts b/hooks/use-model-config.ts index 08c89c4..9904253 100644 --- a/hooks/use-model-config.ts +++ b/hooks/use-model-config.ts @@ -109,9 +109,11 @@ export interface UseModelConfigReturn { models: FlattenedModel[] selectedModel: FlattenedModel | undefined selectedModelId: string | undefined + showUnvalidatedModels: boolean // Actions setSelectedModelId: (modelId: string | undefined) => void + setShowUnvalidatedModels: (show: boolean) => void addProvider: (provider: ProviderName) => ProviderConfig updateProvider: ( providerId: string, @@ -160,6 +162,13 @@ export function useModelConfig(): UseModelConfigReturn { })) }, []) + const setShowUnvalidatedModels = useCallback((show: boolean) => { + setConfig((prev) => ({ + ...prev, + showUnvalidatedModels: show, + })) + }, []) + const addProvider = useCallback( (provider: ProviderName): ProviderConfig => { const newProvider = createProviderConfig(provider) @@ -278,7 +287,9 @@ export function useModelConfig(): UseModelConfigReturn { models, selectedModel, selectedModelId: config.selectedModelId, + showUnvalidatedModels: config.showUnvalidatedModels ?? false, setSelectedModelId, + setShowUnvalidatedModels, addProvider, updateProvider, deleteProvider, diff --git a/lib/i18n/dictionaries/en.json b/lib/i18n/dictionaries/en.json index 2d6b1fe..26557fe 100644 --- a/lib/i18n/dictionaries/en.json +++ b/lib/i18n/dictionaries/en.json @@ -243,6 +243,9 @@ "default": "Default", "serverDefault": "Server Default", "configureModels": "Configure Models...", - "onlyVerifiedShown": "Only verified models are shown" + "onlyVerifiedShown": "Only verified models are shown", + "showUnvalidatedModels": "Show unvalidated models", + "allModelsShown": "All models are shown (including unvalidated)", + "unvalidatedModelWarning": "This model has not been validated" } } diff --git a/lib/i18n/dictionaries/ja.json b/lib/i18n/dictionaries/ja.json index 6b86e4e..39bdac0 100644 --- a/lib/i18n/dictionaries/ja.json +++ b/lib/i18n/dictionaries/ja.json @@ -243,6 +243,9 @@ "default": "デフォルト", "serverDefault": "サーバーデフォルト", "configureModels": "モデルを設定...", - "onlyVerifiedShown": "検証済みのモデルのみ表示" + "onlyVerifiedShown": "検証済みのモデルのみ表示", + "showUnvalidatedModels": "未検証のモデルを表示", + "allModelsShown": "すべてのモデルを表示(未検証を含む)", + "unvalidatedModelWarning": "このモデルは検証されていません" } } diff --git a/lib/i18n/dictionaries/zh.json b/lib/i18n/dictionaries/zh.json index 7b3413c..b7868c4 100644 --- a/lib/i18n/dictionaries/zh.json +++ b/lib/i18n/dictionaries/zh.json @@ -243,6 +243,9 @@ "default": "默认", "serverDefault": "服务器默认", "configureModels": "配置模型...", - "onlyVerifiedShown": "仅显示已验证的模型" + "onlyVerifiedShown": "仅显示已验证的模型", + "showUnvalidatedModels": "显示未验证的模型", + "allModelsShown": "显示所有模型(包括未验证的)", + "unvalidatedModelWarning": "此模型尚未验证" } } diff --git a/lib/types/model-config.ts b/lib/types/model-config.ts index 38b3ba8..ad7fc25 100644 --- a/lib/types/model-config.ts +++ b/lib/types/model-config.ts @@ -40,6 +40,7 @@ export interface MultiModelConfig { version: 1 providers: ProviderConfig[] selectedModelId?: string // Currently selected model's UUID + showUnvalidatedModels?: boolean // Show models that haven't been validated } // Flattened model for dropdown display