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-models-ts

v0.1.2

Published

TypeScript/JavaScript client and model catalog for Ollama Cloud and OpenRouter APIs

Readme

ai-models-ts

Lightweight TypeScript and JavaScript client library and verified free models catalog for Ollama Cloud (https://ollama.com) and OpenRouter (https://openrouter.ai).

npm version License: MIT


Features

  • 🚀 Zero Unnecessary Bloat: No heavy dependencies, no server middleware, no caching layers.
  • Ollama Cloud & OpenRouter Clients: Standalone functions and factory instances for chat completions, live model listing, and capability discovery.
  • 🆓 Free Models Catalog (free-models.ts): Built-in list of verified free models across Ollama Cloud and OpenRouter with context length and capability metadata.
  • 🔄 Live Model Builder: Automated upstream verification script (npm run build:models) that probes live APIs to update free-models.ts.
  • 🌐 Universal Runtime Support: Fully compatible with Node.js (>= 18), Bun, Deno, and Cloudflare Workers (V8 isolates).
  • 📦 Subpath Exports: Import only what you need (/ollama, /openrouter, /models, /types).

Installation

npm install ai-models-ts

Quick Start

1. Ollama Cloud

import { createOllamaClient, ollamaChat } from "ai-models-ts/ollama";

// Factory instance
const ollama = createOllamaClient({ apiKey: process.env.OLLAMA_API_KEY });

// Chat Completion
const response = await ollama.chat({
  model: "gemma4:31b",
  messages: [{ role: "user", content: "Hello from Ollama!" }],
});

const result = await response.json();
console.log(result.choices[0].message.content);

// Live Model Discovery from Endpoint
const models = await (await ollama.listModels()).json();
const tags = await (await ollama.tags()).json();
const show = await (await ollama.show("gemma4:31b")).json();

Standalone Function

import { ollamaChat } from "ai-models-ts/ollama";

const response = await ollamaChat({
  apiKey: process.env.OLLAMA_API_KEY!,
  model: "gemma4:31b",
  messages: [{ role: "user", content: "Hello!" }],
  max_tokens: 100,
});

2. OpenRouter

import { createOpenRouterClient, openRouterChat } from "ai-models-ts/openrouter";

// Factory instance
const openrouter = createOpenRouterClient({
  apiKey: process.env.OPENROUTER_API_KEY,
});

// Chat Completion
const response = await openrouter.chat({
  model: "google/gemma-4-31b-it:free",
  messages: [{ role: "user", content: "Hello from OpenRouter!" }],
});

const result = await response.json();
console.log(result.choices[0].message.content);

// Live Model Discovery from Endpoint
const allModels = await (await openrouter.listModels()).json();

// Check Key Status & Limits
const keyStatus = await (await openrouter.checkKey()).json();
console.log(keyStatus.data.limit_remaining);

OpenRouter currently limits free models to 20 requests per minute and 50 requests per day, or 1,000 per day after at least $10 in credits has been purchased. openRouterChat() therefore spaces free requests sharing an API key in the current runtime by three seconds. A specific free model also gets openrouter/free as an OpenRouter-native model fallback in the same request, so saturation does not create a client-side retry storm. For image requests, text-only fallbacks are removed and the vision-capable free router is used.

Disable either safeguard when raw behavior is required, or configure it:

await openRouterChat({
  apiKey: process.env.OPENROUTER_API_KEY!,
  model: "google/gemma-4-31b-it:free",
  messages: [{ role: "user", content: "Hello" }],
  fallbackModel: false,       // or another OpenRouter model ID
  rateLimitIntervalMs: false, // or a custom minimum interval
});

The in-memory pacer coordinates one Node process, browser context, or Cloudflare Worker isolate. If the same key is used by multiple processes or isolates, enforce the same 20 RPM budget at the shared gateway as well.

References: OpenRouter free-model limits, native model fallbacks, and the capability-aware free router.


3. Models Catalog (free-models.ts)

import {
  MODELS,
  getModels,
  getModel,
  hasModel,
  getVisionModels,
  hasVision,
} from "ai-models-ts/models";

// Get free models (free=true)
const freeModels = getModels({ free: true });
console.log(freeModels.map((m) => m.id));

// Filter free models by provider
const freeOllama = getModels({ provider: "ollama", free: true });
const freeOpenRouter = getModels({ provider: "openrouter", free: true });

// Get all models with vision capability
const visionModels = getVisionModels();
console.log(visionModels.map((m) => `${m.id} (${m.provider})`));

// Check if a model supports vision
console.log(hasVision("gemma4:31b")); // true
console.log(hasVision("google/gemma-4-31b-it:free")); // true
console.log(hasVision("gpt-oss:120b")); // false

// Check if a model exists in the catalog
console.log(hasModel("gemma4:31b")); // true
console.log(hasModel("google/gemma-4-31b-it:free")); // true

// Get metadata for a model
const model = getModel("gemma4:31b");
console.log(model?.contextLength); // 262144
console.log(model?.capabilities); // ["completion", "thinking", "tools", "vision"]

4. Multimodal & Vision Chat

Pass image URLs or base64 data URLs to any vision-capable model:

import { ollamaChat } from "ai-models-ts/ollama";
import { openRouterChat } from "ai-models-ts/openrouter";

const imageUrl = "data:image/png;base64,...";

// Vision completion via Ollama Cloud (gemma4:31b)
const resOllama = await ollamaChat({
  apiKey: process.env.OLLAMA_API_KEY!,
  model: "gemma4:31b",
  messages: [
    {
      role: "user",
      content: [
        { type: "text", text: "Describe what is in this picture:" },
        { type: "image_url", image_url: { url: imageUrl } },
      ],
    },
  ],
});

// Vision completion via OpenRouter (google/gemma-4-31b-it:free)
const resOpenRouter = await openRouterChat({
  apiKey: process.env.OPENROUTER_API_KEY!,
  model: "google/gemma-4-31b-it:free",
  messages: [
    {
      role: "user",
      content: [
        { type: "text", text: "What are the key objects in this image?" },
        { type: "image_url", image_url: { url: "https://example.com/image.jpg" } },
      ],
    },
  ],
});

Streaming Responses

All chat functions support standard Server-Sent Events (SSE) streaming with stream: true:

import { createOllamaClient } from "ai-models-ts/ollama";

const ollama = createOllamaClient({ apiKey: process.env.OLLAMA_API_KEY });

const response = await ollama.chat({
  model: "gemma4:31b",
  messages: [{ role: "user", content: "Tell me a story" }],
  stream: true,
});

const reader = response.body?.getReader();
const decoder = new TextDecoder();

while (reader) {
  const { done, value } = await reader.read();
  if (done) break;
  console.log(decoder.decode(value));
}

Cloudflare Worker Example

import {
  ollamaChat,
  openRouterChat,
  ollamaGetTags,
  ollamaGetVersion,
  getModels,
} from "ai-models-ts";

export interface Env {
  OLLAMA_API_KEY?: string;
  OPENROUTER_API_KEY?: string;
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);

    if (url.pathname === "/api/version") {
      return ollamaGetVersion();
    }

    if (url.pathname === "/api/tags") {
      return ollamaGetTags({ apiKey: env.OLLAMA_API_KEY });
    }

    if (url.pathname === "/v1/models") {
      return new Response(JSON.stringify({ data: getModels({ free: true }) }), {
        headers: { "Content-Type": "application/json" },
      });
    }

    if (url.pathname === "/v1/chat/completions" && request.method === "POST") {
      const body = await request.json();
      if (body.model?.startsWith("openrouter/")) {
        return openRouterChat({ ...body, apiKey: env.OPENROUTER_API_KEY });
      }
      return ollamaChat({ ...body, apiKey: env.OLLAMA_API_KEY });
    }

    return new Response("Not Found", { status: 404 });
  },
};

Rebuilding the Models Catalog

To probe the live upstream APIs of Ollama Cloud and OpenRouter and update src/models/free-models.ts:

npm run build:models

Subpath Exports Map

| Subpath | Description | | :--- | :--- | | ai-models-ts | Main entrypoint re-exporting all modules | | ai-models-ts/ollama | Ollama client, chat, listModels, tags, version, show | | ai-models-ts/openrouter | OpenRouter client, chat, listModels, checkKey | | ai-models-ts/models | MODELS, getModels(), getModel(), hasModel() | | ai-models-ts/types | TypeScript interfaces (ModelInfo, OllamaChatOptions, etc.) |


License

MIT © lgnat