npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

ai-model-selector

v0.1.0

Published

React components and hooks for AI model selection — provider switching, capability filtering, model comparison, and compatibility guards.

Readme

ai-model-selector

License: MIT TypeScript React

React components and hooks for AI model selection. Built for apps that use multiple AI providers (Ollama, Claude, OpenRouter, or your own).

Single-Select Mode

Single-select mode

Compare Mode

Compare mode

See the full Usage Guide for integration walkthrough, screenshots, and MLflow examples.

Features

  • Provider switching — radio-style buttons to switch between AI providers
  • Model dropdown — select element with parameter sizes, capability tags, and exclusion guards
  • Capability filtering — filter models by capability (reasoning, writing, coding, etc.)
  • Compare mode — multi-select checkboxes for side-by-side model comparison
  • Model exclusion — automatically grey out models incompatible with your context (e.g., reasoning models that break JSON parsing)
  • Experiment naming — label comparison runs for tracking with MLflow or similar
  • Fully customizable — override styles, provide custom components, extend the model catalog
  • Headless-friendly — use just the hook and build your own UI
  • Tree-shakeable — import only what you need

Install

npm install ai-model-selector

Quick Start

import {
  useModelSelection,
  ProviderSelector,
  ModelDropdown,
  CapabilityBadges,
  ModelFilterBar,
  ModelCheckboxPanel,
} from "ai-model-selector";

function MyModelPicker() {
  const ms = useModelSelection({
    fetchModels: () => fetch("/api/models").then((r) => r.json()),
    defaultProvider: "ollama",
    excludeMode: "json-output", // grey out reasoning models
    storageKey: "my-app-compare", // persist selections in localStorage
  });

  if (ms.compareMode) {
    return (
      <div>
        <ModelFilterBar
          providerFilters={ms.providerFilters}
          capFilters={ms.capFilters}
          hasActiveFilters={ms.hasActiveFilters}
          onToggleProvider={ms.toggleProviderFilter}
          onToggleCap={ms.toggleCapFilter}
          onClear={ms.clearFilters}
        />
        <ModelCheckboxPanel
          models={ms.allModels}
          filteredIds={ms.filteredIds}
          hasActiveFilters={ms.hasActiveFilters}
          selectedModels={ms.compareModels}
          onToggle={ms.toggleCompareModel}
          isExcluded={ms.isExcluded}
        />
      </div>
    );
  }

  return (
    <div>
      <ProviderSelector
        providers={[
          { value: "ollama", label: "Ollama", badge: "Free" },
          { value: "claude", label: "Claude" },
          { value: "openrouter", label: "OpenRouter", badge: "Free" },
        ]}
        value={ms.provider}
        onChange={ms.setProvider}
      />
      <ModelDropdown
        models={ms.currentModels}
        value={ms.selectedModel}
        onChange={ms.setSelectedModel}
        loading={ms.modelsLoading}
        excludeMode="json-output"
        label="Model"
      />
      <CapabilityBadges
        model={ms.currentModels.find((m) => m.id === ms.selectedModel)}
      />
    </div>
  );
}

Hook-Only Usage

If you want the state management without the UI components:

import { useModelSelection } from "ai-model-selector";

function MyCustomUI() {
  const {
    provider,
    setProvider,
    selectedModel,
    setSelectedModel,
    currentModels,
    compareMode,
    setCompareMode,
    compareModels,
    toggleCompareModel,
    isExcluded,
  } = useModelSelection({
    fetchModels: () => fetch("/api/models").then((r) => r.json()),
  });

  // Build your own UI using the state...
}

Backend-Only (No React)

Use the catalog module for server-side validation without pulling in React:

import { isModelExcluded, getModelMetadata } from "ai-model-selector/catalog";

// Reject incompatible models in your API route
if (isModelExcluded(requestedModel, "json-output")) {
  return { error: "This model cannot produce structured JSON" };
}

// Get model capabilities
const meta = getModelMetadata("gemma3:12b");
// { capabilities: ["general", "writing"], description: "Google all-rounder", parameterSize: "12B" }

Custom Model Catalog

Extend or replace the default catalog:

import { createCatalogLookup, DEFAULT_CATALOG } from "ai-model-selector/catalog";

// Add your own models and keep the defaults as fallback
const { getModelMetadata, isModelExcluded } = createCatalogLookup([
  ["my-custom-model", {
    capabilities: ["coding", "reasoning"],
    description: "My fine-tuned model",
    parameterSize: "13B",
    excludeFromModes: ["json-output"],
  }],
  ...DEFAULT_CATALOG,
]);

Custom Styling

All components accept className props for styling. They use plain CSS class names — works with Tailwind, CSS modules, or any CSS framework.

<ProviderSelector
  providers={providers}
  value={provider}
  onChange={setProvider}
  className="flex gap-4"
  buttonClassName="px-4 py-2 rounded-lg border"
  activeClassName="bg-indigo-100 border-indigo-500 text-indigo-700"
  badgeClassName="text-xs bg-green-100 text-green-700 px-1 rounded"
/>

Custom Checkbox Component

Bring your own checkbox (e.g., shadcn/ui, Radix, Headless UI):

import { Checkbox } from "@/components/ui/checkbox"; // shadcn/ui

<ModelCheckboxPanel
  models={ms.allModels}
  filteredIds={ms.filteredIds}
  hasActiveFilters={ms.hasActiveFilters}
  selectedModels={ms.compareModels}
  onToggle={ms.toggleCompareModel}
  isExcluded={ms.isExcluded}
  renderCheckbox={({ checked, disabled, onChange }) => (
    <Checkbox checked={checked} disabled={disabled} onCheckedChange={onChange} />
  )}
/>

Models API Format

The fetchModels function should return models grouped by provider:

{
  ollama: [
    { id: "gemma3:12b", name: "gemma3:12b", provider: "ollama", capabilities: ["general", "writing"], parameterSize: "12B" },
    { id: "mistral:7b", name: "mistral:7b", provider: "ollama", capabilities: ["writing", "general"], parameterSize: "7B" },
  ],
  claude: [
    { id: "claude-sonnet-4-6", name: "Claude Sonnet 4.6", provider: "claude", capabilities: ["general", "writing", "coding"] },
  ],
}

API Reference

Hook

| Export | Description | |--------|-------------| | useModelSelection(options) | Main hook — manages all selection state | | ModelSelectionState | TypeScript type for the hook's return value |

Components

| Component | Description | |-----------|-------------| | ProviderSelector | Radio-style provider buttons | | ModelDropdown | Select element with exclusion guards | | CapabilityBadges | Colored capability pills | | ModelFilterBar | Provider + capability filter buttons | | ModelCheckboxPanel | Multi-select model list with checkboxes | | ExperimentNameInput | Text field for experiment labeling |

Catalog Functions

| Export | Description | |--------|-------------| | getModelMetadata(id) | Look up capabilities, description, parameter size | | isModelExcluded(id, mode) | Check if a model is excluded from a mode | | createCatalogLookup(catalog) | Create custom lookup functions from your own catalog | | getCapabilityColor(cap) | Get the display color for a capability | | DEFAULT_CATALOG | The built-in model catalog (extensible) | | CAPABILITY_COLORS | Default color map for capabilities |

Types

| Type | Description | |------|-------------| | AIProvider | Provider identifier ("claude" \| "ollama" \| "openrouter" \| string) | | ModelCapability | Capability tag ("reasoning" \| "writing" \| "coding" \| ...) | | ModelMode | Context mode ("json-output" \| "free-text" \| string) | | ModelInfo | Model metadata object | | CatalogEntry | Catalog entry with capabilities and exclusions | | ModelsByProvider | Models grouped by provider key |

Experiment Tracking (MLflow, W&B, etc.)

The experimentName from the hook is a plain string — pass it to your API and use it with any tracking system. The package does NOT include tracking dependencies.

// Frontend: pass experiment name to your API
await fetch("/api/generate", {
  method: "POST",
  body: JSON.stringify({
    model: ms.selectedModel,
    experimentName: ms.experimentName,
    prompt: "...",
  }),
});
# Backend: log to MLflow (or any tracker)
import mlflow

mlflow.set_experiment(request.experiment_name)
with mlflow.start_run():
    mlflow.log_param("model", request.model)
    result = await run_inference(request.model, request.prompt)
    mlflow.log_metric("latency_ms", result.latency_ms)

See the Usage Guide for full Node.js and Python examples.

License

MIT