aiclientjs
v0.1.5
Published
The lightweight, universal AI client for JavaScript & TypeScript. Zero dependencies. Works everywhere.
Maintainers
Readme
npm install aiclientjsimport { ai } from 'aiclientjs';
const { text } = await ai('Explain quantum computing in one sentence');That's it. No boilerplate. No framework lock-in. Works everywhere.
Why aiclientjs?
| | aiclientjs | Vercel AI SDK | Direct API calls |
|---|---|---|---|
| Bundle size | ~8 KB gzipped | ~150 KB+ | N/A |
| Dependencies | 0 | 20+ | varies |
| Providers | OpenAI, Anthropic, Google, Ollama, custom | OpenAI, Anthropic, Google, more | 1 per SDK |
| Runtimes | Node, Deno, Bun, Browser, Edge | Primarily Node/Edge | varies |
| Streaming | for await...of | React hooks / streams | manual SSE parsing |
| TypeScript | First-class, fully typed | Yes | varies |
| Learning curve | 1 function | Framework concepts | Per-provider docs |
Install
npm install aiclientjs # npm
pnpm add aiclientjs # pnpm
yarn add aiclientjs # yarn
bun add aiclientjs # bunQuickstart
1. Set your API key
export OPENAI_API_KEY=sk-...
# or
export ANTHROPIC_API_KEY=sk-ant-...
# or
export GOOGLE_API_KEY=AI...2. Call any model
import { ai } from 'aiclientjs';
// OpenAI (default)
const res = await ai('Hello!');
console.log(res.text);
// Anthropic Claude
const res = await ai('Hello!', { provider: 'anthropic' });
// Google Gemini
const res = await ai('Hello!', { provider: 'google' });
// Local model via Ollama
const res = await ai('Hello!', { provider: 'ollama', model: 'llama3.1' });Streaming
const stream = await ai('Write a poem about code', { stream: true });
for await (const chunk of stream) {
process.stdout.write(chunk); // tokens arrive in real-time
}Collect the full response after streaming
const stream = await ai('Tell me a story', { stream: true });
const response = await stream.response(); // waits for stream to finish
console.log(response.text);
console.log(response.usage);Use with Web APIs (Hono, Express, Next.js)
const stream = await ai('Hello', { stream: true });
return new Response(stream.toReadableStream());Structured Output
Get type-safe JSON responses. Works with JSON Schema or Zod.
With JSON Schema
const res = await ai<{ colors: string[] }>('List 5 colors', {
schema: {
type: 'object',
properties: {
colors: { type: 'array', items: { type: 'string' } },
},
required: ['colors'],
},
});
console.log(res.data.colors); // string[] — fully typedWith Zod (zero-config, no adapter needed)
import { z } from 'zod';
const res = await ai('List 5 colors', {
schema: z.object({
colors: z.array(z.string()),
}),
});
console.log(res.data.colors); // inferred as string[]aiclientjs detects Zod schemas automatically — no plugins, no adapters, no zodToJsonSchema calls.
Tool Calling
Define tools with execute handlers and aiclientjs runs them automatically.
const res = await ai('What is the weather in London?', {
tools: {
getWeather: {
description: 'Get current weather for a city',
parameters: {
type: 'object',
properties: {
city: { type: 'string' },
},
required: ['city'],
},
execute: async ({ city }) => {
const data = await fetch(`https://api.weather.com/${city}`);
return data.json();
},
},
},
});
console.log(res.toolResults); // [{ toolName: 'getWeather', result: { temp: 15 } }]Preconfigured Clients
Create reusable clients with createAIClient:
import { createAIClient } from 'aiclientjs';
const gpt = createAIClient({
provider: 'openai',
model: 'gpt-4o',
system: 'You are a helpful assistant. Be concise.',
temperature: 0.7,
});
const claude = createAIClient({
provider: 'anthropic',
model: 'claude-sonnet-4-20250514',
});
// Use without repeating config
const answer = await gpt('What is 2+2?');
const story = await claude('Tell me a short story', { stream: true });Providers
Built-in
| Provider | Models | Auth |
|---|---|---|
| openai | GPT-4o, GPT-4, o1, etc. | OPENAI_API_KEY |
| anthropic | Claude Sonnet, Opus, Haiku | ANTHROPIC_API_KEY |
| google | Gemini 2.0, 1.5, etc. | GOOGLE_API_KEY |
| ollama | Llama, Mistral, Phi, any local model | No auth needed |
OpenAI-Compatible APIs
Any OpenAI-compatible endpoint works with the openai provider + a custom baseURL:
// Together AI
const res = await ai('Hello', {
provider: 'openai',
baseURL: 'https://api.together.xyz/v1',
apiKey: process.env.TOGETHER_API_KEY,
model: 'meta-llama/Llama-3-70b-chat-hf',
});
// Groq
const res = await ai('Hello', {
provider: 'openai',
baseURL: 'https://api.groq.com/openai/v1',
apiKey: process.env.GROQ_API_KEY,
model: 'llama3-70b-8192',
});Custom Providers
Register your own provider:
import { registerProvider } from 'aiclientjs';
registerProvider('my-provider', {
name: 'my-provider',
async chat(request, config) {
// Implement your provider logic
return { text: '...', toolCalls: [], usage: { ... }, ... };
},
async *stream(request, config) {
// Yield stream events
yield { type: 'text', text: 'Hello' };
yield { type: 'finish', finishReason: 'stop' };
},
});
const res = await ai('Hello', { provider: 'my-provider' });Multi-modal (Vision)
const res = await ai([
{
role: 'user',
content: [
{ type: 'text', text: 'What is in this image?' },
{ type: 'image', url: 'https://example.com/photo.jpg' },
],
},
]);Cancellation
const controller = new AbortController();
// Cancel after 5 seconds
setTimeout(() => controller.abort(), 5000);
const res = await ai('Write a very long essay', {
signal: controller.signal,
});Error Handling
import { ai, AIError } from 'aiclientjs';
try {
const res = await ai('Hello');
} catch (err) {
if (err instanceof AIError) {
console.log(err.code); // 'AUTH_ERROR' | 'RATE_LIMIT' | 'PROVIDER_ERROR' | ...
console.log(err.statusCode); // 401, 429, 500, etc.
console.log(err.provider); // 'openai', 'anthropic', etc.
}
}API Reference
ai(prompt, options?)
| Parameter | Type | Description |
|---|---|---|
| prompt | string \| Message[] | Text prompt or conversation messages |
| options.provider | string | Provider name (default: 'openai') |
| options.model | string | Model ID (default varies by provider) |
| options.apiKey | string | API key (or use env vars) |
| options.baseURL | string | Custom API endpoint |
| options.system | string | System prompt |
| options.stream | boolean | Enable streaming |
| options.schema | JsonSchema \| ZodSchema | Structured output schema |
| options.tools | Record<string, ToolDefinition> | Tool definitions |
| options.temperature | number | Sampling temperature (0-2) |
| options.maxTokens | number | Max response tokens |
| options.signal | AbortSignal | Cancellation signal |
createAIClient(config)
Creates a preconfigured ai function with defaults baked in.
registerProvider(name, provider)
Register a custom provider. See Custom Providers.
Architecture
┌─────────────────────────────────────────┐
│ ai() / client │ ← Simple public API
├─────────────────────────────────────────┤
│ Provider Registry │ ← Strategy pattern
├──────────┬──────────┬───────┬───────────┤
│ OpenAI │Anthropic │Google │ Ollama │ ← Adapters
├──────────┴──────────┴───────┴───────────┤
│ BaseProvider (shared HTTP/SSE) │ ← Template method
├─────────────────────────────────────────┤
│ SSE Parser │ Schema │ Tool Executor │ ← Zero-dep utilities
└─────────────────────────────────────────┘Design principles:
- Zero dependencies — uses native
fetch,ReadableStream,TextDecoder - Strategy pattern — providers are interchangeable strategies
- Template method —
BaseProviderhandles HTTP; subclasses handle serialization - Open/Closed — add providers without modifying core code
- Dependency inversion — core depends on
AIProviderinterface, not implementations
Runtime Support
| Runtime | Supported | |---|---| | Node.js 18+ | Yes | | Deno | Yes | | Bun | Yes | | Cloudflare Workers | Yes | | Browser | Yes | | Vercel Edge | Yes |
License
MIT
