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

@yesimagent/gateway

v0.0.9

Published

An AI SDK model registry. Resolve a `provider:model` reference to a native AI SDK model, with failover groups and circuit breakers on top. Cordis-free — no framework, no service shell.

Readme

@yesimagent/gateway

An AI SDK model registry. Resolve a provider:model reference to a native AI SDK model, with failover groups and circuit breakers on top. Cordis-free — no framework, no service shell.

The gateway owns the boring part of using many providers: building AI SDK providers from configuration, expanding credentials, keeping a catalog of declared models, and offering interchangeable models through a group with health tracking. It hands out whatever the AI SDK factories return — unless a reference pins a think level, it never wraps or reinterprets models.

  • Native models out — every resolve returns what the AI SDK factories built. providerOptions keys, wire protocols, and capabilities behave exactly as the AI SDK documents them. The one exception is a reference that pins a think level (provider:model:high), which adds a thin middleware setting the AI SDK's reasoning call option.
  • Configuration in — a host reads its own file format (yaml, json, …) and hands the parsed shape over. The gateway only defines the shape and expands ${VAR} references against its env.
  • Fail small — bad configuration fails at construction with a GatewayError, not at the first call.

Install

npm add @yesimagent/gateway

Quickstart

import { createGateway } from "@yesimagent/gateway";

const gateway = createGateway({
  config: {
    providers: {
      relay: {
        api: "openai-completions",
        baseUrl: "https://relay.example.com/v1",
        apiKey: "${RELAY_KEY}",
        models: [{ id: "main", name: "Main", contextWindow: 262144 }],
      },
    },
    groups: {
      fast: { strategy: "failover", models: ["relay:main"], circuitBreaker: { failureThreshold: 2, cooldownSeconds: 60 } },
    },
  },
});

const model = gateway.languageModel("relay:main");
const [candidate] = gateway.group("fast").candidates();

apiKey: "${RELAY_KEY}" is read from process.env when the gateway is built; an unset variable fails construction with the variable named.

Configuration

A GatewayConfig has two sections, both optional:

interface GatewayConfig {
  providers?: Record<string, ProviderConfig>;
  groups?: Record<string, GroupConfig>;
}

Providers

Each entry in providers builds one AI SDK provider through a registered api.

interface ProviderConfig {
  api: string; // which ApiFactory builds it, see the table below
  apiKey: string; // required; ${NAME} reads it from the gateway's env
  baseUrl?: string; // omitted means the api's own default endpoint
  headers?: Record<string, string>; // values may use ${NAME} the same way
  settings?: Record<string, unknown>; // spread verbatim into the AI SDK factory
  models?: ModelConfig[]; // what this endpoint serves
}

interface ModelConfig {
  id: string; // model id as the provider knows it, e.g. "gpt-5.1"
  type?: ModelType; // defaults to "language"
  name?: string; // display name
  toolCall?: boolean; // whether the model calls tools
  reasoning?: boolean; // whether the model produces reasoning content
  input?: ModelModality[]; // accepted input modalities
  contextWindow?: number; // total context, in tokens
  maxTokens?: number; // maximum output, in tokens
  dimensions?: number; // embedding models only
}

Everything a model needs is in the configuration: the catalog is built once from models at load time, and gateway.model("relay:main") looks it up. Everything a declaration carries besides its identity becomes the entry's metadata, typed by modality — ModelMetadata<T> is derived from ModelConfig, so a language entry carries toolCall / contextWindow while an image entry carries name alone, and a new config variant needs no extra mapping. There is no discovery, no background refresh, no derived state to invalidate — reconfigure() replaces the whole picture.

Groups

A group is a named set of interchangeable language models:

interface GroupConfig {
  strategy?: "failover" | "round-robin" | "random"; // defaults to "failover"
  models: string[]; // "provider:model" references
  circuitBreaker?: Partial<CircuitBreakerConfig>; // defaults: 3 failures / 60s cooldown
}

gateway.group("fast").candidates() returns the members in the strategy's order, skipping models whose breaker is open. Each candidate carries success() / failure() feedback into the group's breakers. When every breaker is open, the full list is offered anyway — a mute bot is worse than one more doomed attempt.

Every member must resolve when the gateway is built. A group with a typo in it fails at construction, with the reference named.

Think levels

A language reference may pin a think level as a trailing segment — provider:modelId[:level] — with the levels none, minimal, low, medium, high, xhigh, max. The pinned level rides into every call the model sees as the AI SDK's portable reasoning option, which each provider translates to its native controls (reasoningEffort, thinking, thinkingConfig, …).

gateway.languageModel("relay:main:low");
groups:
  fast:
    models: ["relay:main:low", "relay:turbo"] # levels mix freely with plain members
  • The trailing segment only counts when it exactly names a level, so model ids that contain colons (ollama:qwen3:32b) parse as before. The flip side: a model id that itself ends in a level name cannot be referenced.
  • A member pinned at a level is its own candidate and its own breaker entry — the same model at two levels are independent members.
  • When a declaration lists thinking.efforts, a pinned level outside that list fails at construction with the level named.
  • max sits outside the AI SDK spec's enum: OpenAI-family endpoints accept it, other providers emit an unsupported warning and ignore it.

Apis

The api key selects the factory that builds the provider. Five are built in (BUILTIN_APIS is exported, so a host can inspect or spread it):

| api | Factory | Wire | | ---------------------- | --------------------------- | -------------------------------------------------------------------------------- | | openai-completions | @ai-sdk/openai-compatible | /chat/completions; every endpoint reports its own id.chat provider namespace | | openai-responses | @ai-sdk/openai | OpenAI Responses API | | anthropic-messages | @ai-sdk/anthropic | Anthropic Messages | | google-generative-ai | @ai-sdk/google | Gemini | | typesafe-ai | @ai-sdk/typesafe-ai | TypeSafe System One; decision models only |

settings is spread straight into the AI SDK factory, so anything the factory accepts can be passed — authToken for Anthropic enterprise relays, includeUsage for compatible endpoints, and so on. Explicit fields (baseUrl, apiKey, headers, the gateway's fetch) always win over settings.

Custom apis

Anything the built-in dialects don't cover, register through apis — including a provider you built yourself and want to keep a typed reference to:

import { createOpenAI } from "@ai-sdk/openai";
import { createGateway } from "@yesimagent/gateway";

const openai = createOpenAI({ apiKey: process.env.OPENAI_API_KEY });

const gateway = createGateway({
  config: { providers: { openai: { api: "openai", apiKey: "unused", models: [{ id: "gpt-5.1" }] } } },
  apis: { openai: () => openai },
});

// The concrete instance is yours, so provider-native tools stay fully typed:
const tools = { web_search: openai.tools.webSearch({}) };

Gateway

interface Gateway {
  defineApi(name: string, factory: ApiFactory): () => void;
  provider(id: string): ProviderV4 | undefined; // for ProviderV4 parts the gateway doesn't model
  readonly apis: readonly string[];
  reconfigure(config: GatewayConfig): void;

  languageModel(name: string): LanguageModelV4;
  embeddingModel(name: string): EmbeddingModelV4;
  imageModel(name: string): ImageModelV4;
  speechModel(name: string): SpeechModelV4;
  transcriptionModel(name: string): TranscriptionModelV4;
  rerankingModel(name: string): RerankingModelV4;
  decisionModel(name: string): DecisionModelV4;
  group(name: string): Group;
  groups(): readonly string[];

  models<T extends ModelType = ModelType>(type?: T): readonly Model<T>[]; // the catalog, for host-side decisions
  model(name: string): Model | undefined;
  providers(): readonly string[];
}
  • models("language") narrows both halves of an entry: the result is Model<"language">, whose metadata is ModelMetadata<"language">. models() without an argument returns the whole catalog.
  • languageModel("relay:main") resolves and builds; a typo, a wrong modality, or an unlisted model throws a GatewayError naming the reference.
  • model("relay:main") is the pure catalog query: undefined when nothing is declared under that reference. Hosts that want pre-flight validation check it before resolving.
  • provider("relay") hands back the AI SDK provider for the parts the gateway doesn't model, such as files() and skills().

License

MIT