From 26573277e192ae557fe0c07bf927bf13064dc316 Mon Sep 17 00:00:00 2001 From: xKevIsDev Date: Thu, 17 Jul 2025 23:57:24 +0100 Subject: [PATCH] feat: add filter for free models in ModelSelector component for OpenRouter - Introduced a helper function `isModelLikelyFree` to identify models that are free based on their label or name. - Added a toggle button to filter models, allowing users to view only free models when using the OpenRouter provider. - Updated the model filtering logic to incorporate the free models filter and adjusted the UI to reflect the count of free models found. - Reset the free models filter when the provider changes to ensure accurate results. --- app/components/chat/ModelSelector.tsx | 80 ++++++++++++++++++++++++--- 1 file changed, 72 insertions(+), 8 deletions(-) diff --git a/app/components/chat/ModelSelector.tsx b/app/components/chat/ModelSelector.tsx index 8d38b25..a15bce4 100644 --- a/app/components/chat/ModelSelector.tsx +++ b/app/components/chat/ModelSelector.tsx @@ -15,6 +15,21 @@ interface ModelSelectorProps { modelLoading?: string; } +// Helper function to determine if a model is likely free +const isModelLikelyFree = (model: ModelInfo, providerName?: string): boolean => { + // OpenRouter models with zero pricing in the label + if (providerName === 'OpenRouter' && model.label.includes('in:$0.00') && model.label.includes('out:$0.00')) { + return true; + } + + // Models with "free" in the name or label + if (model.name.toLowerCase().includes('free') || model.label.toLowerCase().includes('free')) { + return true; + } + + return false; +}; + export const ModelSelector = ({ model, setModel, @@ -36,6 +51,7 @@ export const ModelSelector = ({ const providerSearchInputRef = useRef(null); const providerOptionsRef = useRef<(HTMLDivElement | null)[]>([]); const providerDropdownRef = useRef(null); + const [showFreeModelsOnly, setShowFreeModelsOnly] = useState(false); useEffect(() => { const handleClickOutside = (event: MouseEvent) => { @@ -57,19 +73,31 @@ export const ModelSelector = ({ const filteredModels = [...modelList] .filter((e) => e.provider === provider?.name && e.name) - .filter( - (model) => + .filter((model) => { + // Apply free models filter + if (showFreeModelsOnly && !isModelLikelyFree(model, provider?.name)) { + return false; + } + + // Apply search filter + return ( model.label.toLowerCase().includes(modelSearchQuery.toLowerCase()) || - model.name.toLowerCase().includes(modelSearchQuery.toLowerCase()), - ); + model.name.toLowerCase().includes(modelSearchQuery.toLowerCase()) + ); + }); const filteredProviders = providerList.filter((p) => p.name.toLowerCase().includes(providerSearchQuery.toLowerCase()), ); + // Reset free models filter when provider changes + useEffect(() => { + setShowFreeModelsOnly(false); + }, [provider?.name]); + useEffect(() => { setFocusedModelIndex(-1); - }, [modelSearchQuery, isModelDropdownOpen]); + }, [modelSearchQuery, isModelDropdownOpen, showFreeModelsOnly]); useEffect(() => { setFocusedProviderIndex(-1); @@ -384,7 +412,36 @@ export const ModelSelector = ({ role="listbox" id="model-listbox" > -
+
+ {/* Free Models Filter Toggle - Only show for OpenRouter */} + {provider?.name === 'OpenRouter' && ( +
+ + {showFreeModelsOnly && ( + + {filteredModels.length} free model{filteredModels.length !== 1 ? 's' : ''} + + )} +
+ )} + + {/* Search Input */}
Loading...
) : filteredModels.length === 0 ? ( -
No models found
+
+ {showFreeModelsOnly ? 'No free models found' : 'No models found'} +
) : ( filteredModels.map((modelOption, index) => (
- {modelOption.label} +
+ {modelOption.label} + {isModelLikelyFree(modelOption, provider?.name) && ( + + )} +
)) )}