npm package: @sdkrouter/client
Description: TypeScript client for SDKRouter — LLM, Audio, Vision, CDN, Search & more
Author: CMDOP
Latest version: 0.2.19

# @sdkrouter/client

Unified TypeScript SDK for AI services. Access 300+ LLM models, vision, audio, image generation, search, CDN, and more through a single interface.

## Installation

```bash
npm install @sdkrouter/client
# or
pnpm add @sdkrouter/client
```

## Quick Start

```ts
import { SDKRouterClient, Model } from '@sdkrouter/client';

const client = new SDKRouterClient({ apiKey: 'your-api-key' });

const response = await client.chat.create({
  model: Model.cheap(),
  messages: [{ role: 'user', content: 'Hello!' }],
});
console.log(response.choices[0]?.message?.content);
```

## Features

| Feature | Description | Access |
|---------|-------------|--------|
| **Chat** | OpenAI-compatible completions, streaming | `client.chat` |
| **Structured Output** | Zod schemas, JSON extraction | `client.parse()` |
| **Audio** | TTS (buffered/streaming/realtime), STT, WebSocket streaming | `client.audio` |
| **Vision** | Image analysis, OCR | `client.vision` |
| **Image Gen** | AI image generation, crop_to, preset management | `client.imageGen` |
| **Search** | Web search with modes (research, analyze, etc.) | `client.search` |
| **CDN** | File storage, upload from URL | `client.cdn` |
| **Shortlinks** | URL shortening | `client.shortlinks` |
| **Cleaner** | HTML cleaning | `client.cleaner` |
| **Models** | LLM model listing, cost calculation | `client.llmModels` |
| **Keys** | API key management | `client.keys` |
| **Payments** | Crypto payments | `client.payments` |
| **Proxies** | Proxy management | `client.proxies` |
| **Embeddings** | Text embeddings | `client.embeddings` |

## Model Routing

Smart model selection with IDE autocomplete:

```ts
import { Model } from '@sdkrouter/client';

Model.cheap()                    // Lowest cost
Model.smart()                    // Highest quality
Model.balanced()                 // Best value
Model.fast()                     // Fastest

// With capabilities
Model.cheap({ vision: true })    // + vision
Model.smart({ tools: true })     // + function calling
Model.balanced({ json: true })   // + JSON mode

// Categories
Model.smart({ code: true })      // Coding
Model.cheap({ reasoning: true }) // Problem solving
```

## Chat

```ts
// Buffered
const response = await client.chat.create({
  model: Model.balanced({ tools: true }),
  messages: [
    { role: 'system', content: 'You are helpful.' },
    { role: 'user', content: 'Hello!' },
  ],
});

// Streaming
for await (const chunk of client.chat.stream({
  model: Model.fast(),
  messages: [{ role: 'user', content: 'Tell me a story' }],
})) {
  process.stdout.write(chunk.choices[0]?.delta?.content ?? '');
}
```

## Structured Output

```ts
import { z } from 'zod';

const Sentiment = z.object({
  sentiment: z.enum(['positive', 'negative', 'neutral']),
  confidence: z.number(),
  keywords: z.array(z.string()),
});

const result = await client.parse({
  model: Model.balanced({ json: true }),
  messages: [{ role: 'user', content: 'Analyze: "This product is amazing!"' }],
  responseFormat: Sentiment,
});

console.log(result.parsed);
// { sentiment: 'positive', confidence: 0.95, keywords: ['amazing'] }
```

## Audio

```ts
import { AudioModel } from '@sdkrouter/client';

// Text-to-Speech (buffered)
const { audioEl, analysis } = await client.audio.speech({
  model: AudioModel.quality(),
  input: 'Hello world!',
  voice: 'nova',
});
audioEl.play();

// Text-to-Speech (real-time streaming)
const audioEl = await client.audio.speechStreamRealtime({
  model: AudioModel.quality({ streaming: true }),
  input: 'Hello world!',
  voice: 'nova',
});

// Speech-to-Text
const result = await client.audio.transcribe({
  file: audioBlob,
  model: AudioModel.cheap(),
});
console.log(result.text);

// Streaming STT (WebSocket)
const stream = client.audio.transcribeStream(
  { model: 'whisper-1', language: 'en' },
  {
    onInterim: (text) => console.log('...', text),
    onFinal: (text) => console.log('Final:', text),
  },
);
stream.sendAudio(pcmChunk);
stream.stop();
```

## Vision

```ts
const result = await client.vision.analyzeCreate({
  image_url: 'https://example.com/photo.jpg',
  prompt: 'Describe this image',
});
console.log(result.description);

// OCR
const ocr = await client.vision.ocrCreate({
  image_url: 'https://example.com/document.jpg',
});
console.log(ocr.text);
```

## Image Generation

```ts
import { ImageGenEnums } from '@sdkrouter/client';

// Basic generation
const result = await client.imageGen.generateCreate({
  prompt: 'A sunset over mountains',
  model: '@balanced',
  size: '1024x1024',
});
console.log(result.image_cdn_url);

// With crop_to (e.g. OG image format)
const og = await client.imageGen.generateCreate({
  prompt: 'A futuristic cityscape',
  model: '@cheap',
  size: '1792x1024',
  crop_to: '1200x630',
});
console.log(og.image_cdn_url);

// Using a saved preset
const withPreset = await client.imageGen.generateCreate({
  prompt: 'Abstract art',
  preset: 'og-image',
});

// Quality + style
const styled = await client.imageGen.generateCreate({
  prompt: 'A portrait',
  quality: ImageGenEnums.ImageGenPresetQuality.HD,
  style: ImageGenEnums.ImageGenPresetStyle.VIVID,
});
```

### Async Generation

```ts
// Start async job
const job = await client.imageGen.generateAsyncCreate({
  prompt: 'A mountain landscape',
  size: '1792x1024',
  crop_to: '1200x630',
});
console.log(job.id, job.status);

// Poll for status
const status = await client.imageGen.statusRetrieve(job.id);
if (status.status === 'completed') {
  console.log(status.image_cdn_url);
}
```

### Presets

```ts
// Create preset
const preset = await client.imageGen.presetsCreate({
  name: 'OG Image',
  slug: 'og-image',
  size: '1792x1024',
  crop_to: '1200x630',
  quality: ImageGenEnums.ImageGenPresetQuality.HD,
});

// List presets
const list = await client.imageGen.presetsList();

// Get by ID
const p = await client.imageGen.presetsRetrieve(preset.id);

// Full update (PUT)
await client.imageGen.presetsUpdate(preset.id, {
  name: 'OG Image v2',
  slug: 'og-image',
  size: '1792x1024',
  crop_to: '1200x630',
});

// Partial update (PATCH)
await client.imageGen.presetsPartialUpdate(preset.id, { name: 'OG Image v3' });

// Delete
await client.imageGen.presetsDestroy(preset.id);
```

## Search

```ts
// Web search
const results = await client.search.queryCreate({
  query: 'TypeScript best practices 2025',
});
console.log(results.answer);

// Fetch & analyze URL
const page = await client.search.fetchCreate({
  url: 'https://example.com/article',
  prompt: 'Summarize this article',
});
```

## CDN

```ts
// Upload file
const file = await client.cdn.create({ file: blob });
console.log(file.url);

// List files
const files = await client.cdn.list();

// Delete
await client.cdn.destroy('uuid');
```

## Embeddings

```ts
// Via sdkrouter proxy (default, single key)
const client = new SDKRouterClient({ apiKey: 'your-sdkrouter-key' });
const result = await client.embeddings.create({
  input: ['Hello world', 'Semantic search'],
  model: 'openai/text-embedding-3-small',
});
const vectors = result.data.map(d => d.embedding);

// Directly via OpenAI key
const client = new SDKRouterClient({
  apiKey: 'sk-...',
  llmUrl: 'https://api.openai.com/v1',
});
const result = await client.embeddings.create({
  input: 'Hello world',
  model: 'text-embedding-3-small',
});

// Directly via OpenRouter key
const client = new SDKRouterClient({
  apiKey: 'sk-or-...',
  llmUrl: 'https://openrouter.ai/api/v1',
});
const result = await client.embeddings.create({
  input: 'Hello world',
  model: 'openai/text-embedding-3-small',
});
```

| Model | Dimensions |
|-------|-----------|
| `openai/text-embedding-3-small` | 1536 (default) |
| `openai/text-embedding-3-large` | 3072 |
| `openai/text-embedding-ada-002` | 1536 (legacy) |

## Provider Selection

By default the server auto-detects the provider from the model name. You can override this explicitly:

```ts
// Client-level — all requests go through this provider
const client = new SDKRouterClient({
  apiKey: 'your-key',
  provider: 'openrouter',
});

// Per-request override
const response = await client.chat.create({
  model: 'qwen3-max',
  messages: [{ role: 'user', content: 'Hi' }],
  provider: 'alibaba', // overrides client-level
});

// Works with streaming too
for await (const chunk of client.chat.stream({
  model: 'qwen3-max',
  messages: [{ role: 'user', content: 'Hi' }],
  provider: 'alibaba',
})) {
  process.stdout.write(chunk.choices[0]?.delta?.content ?? '');
}
```

Available providers: `openrouter`, `openai`, `anthropic`, `alibaba` (more coming).

## Configuration

```ts
const client = new SDKRouterClient({
  apiKey: 'your-key',
  llmUrl: 'https://...',      // default: https://llm.sdkrouter.com/v1
  apiUrl: 'https://...',      // default: https://api.sdkrouter.com
  audioUrl: 'https://...',    // default: https://audio.sdkrouter.com/v1
  env: 'development',         // use localhost URLs for local dev
  timeout: 60_000,
});

// Environment variables
// SDKROUTER_API_KEY
// SDKROUTER_LLM_URL
// SDKROUTER_API_URL
// SDKROUTER_AUDIO_URL
```

## Error Handling

```ts
import {
  SDKRouterError,
  AuthenticationError,
  RateLimitError,
  NotFoundError,
} from '@sdkrouter/client';

try {
  await client.chat.create({ ... });
} catch (err) {
  if (err instanceof AuthenticationError) {
    // 401 — invalid or missing API key
  } else if (err instanceof RateLimitError) {
    // 429 — too many requests
  } else if (err instanceof SDKRouterError) {
    console.log(err.status, err.body);
  }
}
```

## Supported Providers

- **OpenAI**: GPT-4.5, GPT-4o, o3, o1
- **Anthropic**: Claude Opus 4.5, Claude Sonnet 4
- **Google**: Gemini 2.5 Pro, Gemini 2.0 Flash
- **Meta**: Llama 4, Llama 3.3
- **Mistral**: Mistral Large, Codestral
- **DeepSeek**: DeepSeek V3, R1
- And 300+ more via OpenRouter

## License

MIT