Files
simple-ai-provider/README.md
Jan-Marlon Leibl c8c579da8b docs: rewrite README
Full rewrite focused on what consumers actually need: install, a
working snippet per provider, and the public API surface.

Changes vs the previous version:
- Document ClaudeCodeProvider, including the subscription-via-CLI
  auth path that the new provider enables.
- Remove the ProviderRegistry section (the class was removed in R1).
- Drop the stale Provider Comparison and Detailed Capabilities
  tables; vendor capabilities and model lists move too fast for a
  README to track.
- Remove the inaccurate Zero Dependencies and Comprehensive Testing
  claims (post-refactor 44/91 tests need updating).
- Refresh default models (Gemini 1.5 -> 2.5) and the package list to
  match the current build.
- Add a brief Architecture note covering the base hooks introduced
  in R2 and the OpenWebUI strategy split from R3.

README is 315 lines (was 701).
2026-05-21 14:25:01 +02:00

316 lines
9.9 KiB
Markdown

# 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
```bash
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
```typescript
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)
```typescript
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.
```typescript
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:
```typescript
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
```typescript
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)
```typescript
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)
```typescript
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:
```typescript
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.
```typescript
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:
```typescript
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`:
```typescript
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:
```typescript
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()`:
```typescript
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
```bash
git clone https://gitea.jleibl.net/jleibl/simple-ai-provider.git
cd simple-ai-provider
bun install
bun run build
```
Examples live in `examples/`:
```bash
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](LICENSE).
## Links
- [Anthropic Claude API](https://docs.anthropic.com/claude/reference/)
- [Claude Code SDK](https://docs.anthropic.com/claude/docs/claude-code-sdk)
- [OpenAI API](https://platform.openai.com/docs/)
- [Google Gen AI SDK](https://ai.google.dev/)
- [OpenWebUI](https://openwebui.com/)
- [Repository](https://gitea.jleibl.net/jleibl/simple-ai-provider)