Overview
The useModels hook fetches available AI models from the OpenRouter API and provides filtering capabilities. It handles model pagination, search, and filtering by provider, context length, and price range.
Import
import { useModels } from '@/hooks/useModels';
Usage
const {
models,
allModels,
loading,
error,
filters,
availableProviders,
updateFilters,
refreshModels,
} = useModels();
Return Values
Filtered array of available models based on current filters. This is the primary array to use for displaying models.
Unfiltered array of all available models. Useful for statistics or when you need the complete list.
True while models are being fetched from the API.
Error message if model fetching failed, null otherwise.
Current filter state including searchTerm, provider, contextLength, and priceRange.
Sorted array of unique provider names extracted from available models (e.g., [‘anthropic’, ‘openai’, ‘google’]).
updateFilters
(newFilters: Partial<ModelFilters>) => void
Function to update filter state. Merges new filters with existing ones.
Function to manually refresh the models list from the API.
Methods
updateFilters
Updates the current filter state.
updateFilters(newFilters: Partial<ModelFilters>): void
newFilters
Partial<ModelFilters>
required
Object containing filter properties to update. Only provided properties are updated.
Example:
const { updateFilters } = useModels();
// Search for models
updateFilters({ searchTerm: 'claude' });
// Filter by provider
updateFilters({ provider: 'anthropic' });
// Filter by context length
updateFilters({ contextLength: 'long' });
// Filter by price range
updateFilters({ priceRange: 'free' });
// Combine multiple filters
updateFilters({
provider: 'openai',
contextLength: 'medium',
priceRange: 'medium',
});
refreshModels
Manually refreshes the models list from the OpenRouter API.
await refreshModels(): Promise<void>
Example:
const { refreshModels, loading } = useModels();
const handleRefresh = async () => {
await refreshModels();
console.log('Models refreshed!');
};
Types
ModelFilters
interface ModelFilters {
searchTerm: string;
provider: string | 'all';
contextLength: 'all' | 'short' | 'medium' | 'long';
priceRange: 'all' | 'free' | 'low' | 'medium' | 'high';
}
Text to search in model ID, name, and description.
Provider to filter by (e.g., ‘anthropic’, ‘openai’) or ‘all’ for no filtering.
contextLength
'all' | 'short' | 'medium' | 'long'
short: ≤ 8,192 tokens
medium: 8,193 - 32,768 tokens
long: > 32,768 tokens
all: No filtering
priceRange
'all' | 'free' | 'low' | 'medium' | 'high'
Price category determined by the getPriceCategory() function. Based on model pricing from OpenRouter.
OpenRouterModel
interface OpenRouterModel {
id: string;
name?: string;
description?: string;
context_length?: number;
pricing?: {
prompt: string;
completion: string;
};
top_provider?: {
context_length: number;
max_completion_tokens: number;
};
architecture?: {
modality: string;
tokenizer: string;
instruct_type?: string;
};
}
Filtering Logic
Search Term Filtering
Searches across multiple model properties (case-insensitive):
model.id.toLowerCase().includes(searchTerm) ||
model.name?.toLowerCase().includes(searchTerm) ||
model.description?.toLowerCase().includes(searchTerm)
Provider Filtering
Extracts provider from model ID:
const provider = model.id.split('/')[0]; // e.g., 'anthropic' from 'anthropic/claude-3.5-sonnet'
Context Length Filtering
switch (contextLength) {
case 'short':
return contextLength <= 8192;
case 'medium':
return contextLength > 8192 && contextLength <= 32768;
case 'long':
return contextLength > 32768;
}
Price Range Filtering
Uses the getPriceCategory() function from the models API service to categorize models.
Data Loading
The hook attempts to load all models using pagination, with fallback:
try {
allModels = await fetchAllAvailableModels(); // Paginated fetch
} catch {
allModels = await fetchAvailableModels(); // Single page fallback
}
If both fail, returns an empty array with an error message.
Example: Model Selector with Filters
import { useModels } from '@/hooks/useModels';
function ModelSelector() {
const {
models,
loading,
error,
filters,
availableProviders,
updateFilters,
refreshModels,
} = useModels();
if (loading) return <div>Loading models...</div>;
if (error) return <div>Error: {error}</div>;
return (
<div>
<input
type="text"
placeholder="Search models..."
value={filters.searchTerm}
onChange={(e) => updateFilters({ searchTerm: e.target.value })}
/>
<select
value={filters.provider}
onChange={(e) => updateFilters({ provider: e.target.value })}
>
<option value="all">All Providers</option>
{availableProviders.map((provider) => (
<option key={provider} value={provider}>
{provider}
</option>
))}
</select>
<select
value={filters.contextLength}
onChange={(e) => updateFilters({ contextLength: e.target.value })}
>
<option value="all">All Context Lengths</option>
<option value="short">Short (≤8K)</option>
<option value="medium">Medium (8K-32K)</option>
<option value="long">Long (>32K)</option>
</select>
<select
value={filters.priceRange}
onChange={(e) => updateFilters({ priceRange: e.target.value })}
>
<option value="all">All Prices</option>
<option value="free">Free</option>
<option value="low">Low</option>
<option value="medium">Medium</option>
<option value="high">High</option>
</select>
<button onClick={refreshModels}>Refresh Models</button>
<div>
<p>Found {models.length} models</p>
{models.map((model) => (
<div key={model.id}>
<h3>{model.name || model.id}</h3>
<p>{model.description}</p>
<p>Context: {model.context_length?.toLocaleString()} tokens</p>
</div>
))}
</div>
</div>
);
}
Example: Provider Statistics
import { useModels } from '@/hooks/useModels';
function ProviderStats() {
const { allModels, availableProviders } = useModels();
const stats = availableProviders.map((provider) => ({
name: provider,
count: allModels.filter((m) => m.id.startsWith(provider + '/')).length,
}));
return (
<div>
<h2>Models by Provider</h2>
{stats.map((stat) => (
<div key={stat.name}>
{stat.name}: {stat.count} models
</div>
))}
</div>
);
}
Notes
- Models are fetched once on mount and cached in state
- Use
refreshModels() to manually update the list
- Filtering is computed via
useMemo for performance
- All filtering is client-side
- Empty model list with error message if API fetch fails
- Provider names are extracted from model IDs (format:
provider/model-name)