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

simple-ai-provider

v2.1.0

Published

A simple and extensible AI provider package for easy integration of multiple AI services

Downloads

737

Readme

Simple AI Provider

A type-safe TypeScript library with a single API surface for Claude (Anthropic), OpenAI, Google Gemini, OpenWebUI, and Claude Code (subscription-friendly via local CLI).

The same complete() / stream() interface works across every provider, with consistent error types, streaming, and optional structured (typed JSON) output.

Install

npm install simple-ai-provider
# or
bun add simple-ai-provider

Requires Node ≥ 18 (or Bun). TypeScript ≥ 5 is recommended as a peer dependency.

Quick start

import { ClaudeProvider } from 'simple-ai-provider';

const claude = new ClaudeProvider({ apiKey: process.env.ANTHROPIC_API_KEY! });
await claude.initialize();

const response = await claude.complete({
  messages: [
    { role: 'system', content: 'You are a helpful assistant.' },
    { role: 'user', content: 'Explain TypeScript in one sentence.' }
  ],
  maxTokens: 200
});

console.log(response.content);
console.log(response.usage);   // { promptTokens, completionTokens, totalTokens }

Every provider follows the same pattern: construct, initialize(), then complete() or stream().

Providers

Claude (Anthropic API)

import { ClaudeProvider } from 'simple-ai-provider';

const claude = new ClaudeProvider({
  apiKey: process.env.ANTHROPIC_API_KEY!,
  defaultModel: 'claude-3-5-sonnet-20241022', // optional
  version: '2023-06-01',                       // optional
  timeout: 30_000,                             // optional, ms
  maxRetries: 3                                // optional
});

Claude Code (subscription via local CLI)

ClaudeCodeProvider wraps @anthropic-ai/claude-agent-sdk, which authenticates through the local claude CLI. This is the supported path for Claude Pro / Max subscribers who don't have a console API key.

Setup: install the CLI and run claude login once. No API key required.

import { ClaudeCodeProvider } from 'simple-ai-provider';

const claude = new ClaudeCodeProvider({});           // uses local credentials
await claude.initialize();

const response = await claude.complete({
  messages: [{ role: 'user', content: 'Hello!' }]
});

You can still pass apiKey to override (it's set as ANTHROPIC_API_KEY for the SDK). Optional config:

new ClaudeCodeProvider({
  defaultModel: 'sonnet',  // 'sonnet' | 'opus' | 'haiku' | 'inherit' | full model ID
  maxTurns: 1,             // 1 for plain completion; raise for agent/tool loops
  allowedTools: [],        // tool names to enable (default: none)
  cwd: process.cwd()       // working directory for the agent
});

Trade-offs to know:

  • Requires claude CLI installed on the host. Not ideal for typical server deployments.
  • Higher latency than the direct API (spawns a CLI process per request).
  • Streaming yields text as the SDK emits successive assistant messages, not token-by-token deltas.

OpenAI

import { OpenAIProvider } from 'simple-ai-provider';

const openai = new OpenAIProvider({
  apiKey: process.env.OPENAI_API_KEY!,
  defaultModel: 'gpt-4o',
  organization: 'org-...',     // optional
  project: 'proj-...'          // optional
});

baseUrl is supported for OpenAI-compatible endpoints.

Gemini (Google)

import { GeminiProvider } from 'simple-ai-provider';

const gemini = new GeminiProvider({
  apiKey: process.env.GOOGLE_AI_API_KEY!,
  defaultModel: 'gemini-2.5-flash',
  safetySettings: [/* SafetySetting[] from @google/genai */],
  generationConfig: {
    temperature: 0.7,
    topP: 0.8,
    topK: 40,
    maxOutputTokens: 1000
  }
});

Backed by @google/genai (the successor to the deprecated @google/generative-ai).

OpenWebUI (local / self-hosted)

import { OpenWebUIProvider } from 'simple-ai-provider';

const openwebui = new OpenWebUIProvider({
  apiKey: 'your-bearer-token',          // from Settings > Account in OpenWebUI
  baseUrl: 'http://localhost:3000',
  defaultModel: 'llama3.1:latest',
  useOllamaProxy: false,                // false: OpenWebUI chat API (default)
                                        // true:  direct Ollama proxy
  dangerouslyAllowInsecureConnections: true
});

useOllamaProxy flips between two internal strategies — the OpenAI-compatible chat completions endpoint and the direct Ollama generate endpoint.

Streaming

Identical shape across providers:

for await (const chunk of provider.stream({ messages, maxTokens: 200 })) {
  if (!chunk.isComplete) {
    process.stdout.write(chunk.content);
  } else {
    console.log('\n', chunk.usage);
  }
}

Structured output

Ask any provider for a typed JSON response. The library injects a system prompt describing the expected shape and parses the result.

import { createResponseType } from 'simple-ai-provider';

interface UserProfile {
  name: string;
  age: number;
  hobbies: string[];
}

const profileType = createResponseType<UserProfile>(
  'A user profile with name, age, and hobbies',
  { name: 'Alice', age: 30, hobbies: ['climbing', 'photography'] }
);

const response = await claude.complete({
  messages: [{ role: 'user', content: 'Generate a fictional user profile.' }],
  responseType: profileType
});

// response.content is typed as UserProfile
// response.rawContent is the original string
console.log(response.content.name);

This also works with stream() — chunks deliver text, then the final chunk parses.

Factory functions

If you prefer a single entry point:

import { createProvider } from 'simple-ai-provider';

const claude    = createProvider('claude',      { apiKey: '…' });
const openai    = createProvider('openai',      { apiKey: '…' });
const gemini    = createProvider('gemini',      { apiKey: '…' });
const openwebui = createProvider('openwebui',   { apiKey: '…', baseUrl: '…' });
const claudeCode = createProvider('claude-code', {});

Per-provider shortcut factories also exist: createClaudeProvider, createOpenAIProvider, createGeminiProvider, createOpenWebUIProvider, createClaudeCodeProvider.

Error handling

Every error thrown by the library is an AIProviderError with a typed .type and optional .statusCode:

import { AIProviderError, AIErrorType } from 'simple-ai-provider';

try {
  await provider.complete({ messages: [...] });
} catch (error) {
  if (error instanceof AIProviderError) {
    switch (error.type) {
      case AIErrorType.AUTHENTICATION:   /* bad API key, expired token */ break;
      case AIErrorType.RATE_LIMIT:       /* slow down */ break;
      case AIErrorType.MODEL_NOT_FOUND:  /* wrong model name */ break;
      case AIErrorType.INVALID_REQUEST:  /* bad input */ break;
      case AIErrorType.NETWORK:          /* transient connectivity */ break;
      case AIErrorType.TIMEOUT:          /* slow request */ break;
      case AIErrorType.UNKNOWN:          /* fall back */ break;
    }
    console.error(error.statusCode, error.originalError);
  }
}

Configuration reference

All providers share these base options:

| Option | Type | Default | Notes | |--------------|----------|-----------|----------------------------------------| | apiKey | string | required¹ | ¹ Optional for ClaudeCodeProvider | | baseUrl | string | provider default | Custom or self-hosted endpoint | | timeout | number | 30000 | Request timeout in ms | | maxRetries | number | 3 | SDK-level retry attempts |

Each provider adds its own config (see the sections above).

TypeScript

Full type definitions ship with the package. The main types you'll use:

import type {
  AIMessage,
  CompletionParams,
  CompletionResponse,
  CompletionChunk,
  TokenUsage,
  ProviderInfo,
  ResponseType,

  // Per-provider config interfaces
  ClaudeConfig,
  ClaudeCodeConfig,
  OpenAIConfig,
  GeminiConfig,
  OpenWebUIConfig
} from 'simple-ai-provider';

CompletionResponse<T> is generic — when you pass responseType, content is T (and the original string is preserved on rawContent).

Provider metadata

Every provider exposes getInfo():

const info = provider.getInfo();
//   { name, version, models, maxContextLength, supportsStreaming, capabilities }

Model lists in info.models are example values — refer to each vendor's docs for the current authoritative list. The library accepts any model string the underlying SDK supports.

Architecture (brief)

The library uses a template-method base class (BaseAIProvider) that owns the public lifecycle (initialize, complete, stream), input validation, response-type parsing, and error normalization. Each provider supplies:

  • doInitialize() / doComplete() / doStream() — the actual SDK calls
  • getModelNamePatterns() — regexes for naming-convention warnings
  • sendValidationProbe() — minimal request used during initialize()
  • mapProviderError() / providerErrorMessages() — provider-specific error translation

OpenWebUIProvider additionally uses an internal strategy split (OpenWebUIChatStrategy vs OpenWebUIOllamaStrategy) selected by useOllamaProxy.

Development

git clone https://gitea.jleibl.net/jleibl/simple-ai-provider.git
cd simple-ai-provider
bun install
bun run build

Examples live in examples/:

bun run examples/basic-usage.ts
bun run examples/multi-provider.ts
bun run examples/structured-response-types.ts

Tests in tests/ use Bun's test runner (bun test). Note: post-refactor some tests need updating before they pass cleanly.

License

MIT — see LICENSE.

Links