Now Available
Pendra Node.js SDK
TypeScript-first client for sovereign UK inference. Streaming support, zero runtime dependencies. Node.js 18+.
Quick Start
import Pendra from 'pendra';
const client = new Pendra({
apiKey: 'pdr_sk_...', // or set PENDRA_API_KEY env var
});
const response = await client.chat.completions.create({
model: 'llama3.2',
messages: [{ role: 'user', content: 'Hello!' }],
});
console.log(response.choices[0].message.content); Streaming
Stream responses token by token using async iteration.
const stream = await client.chat.completions.create({
model: 'llama3.2',
messages: [{ role: 'user', content: 'Write a poem' }],
stream: true,
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? '');
} Image Generation
Generate images from a text prompt. Returns base64-encoded PNGs by default — decode with Buffer.from(b64, 'base64') and write to disk, or set response_format: 'url' when supported by the model.
import { writeFileSync } from 'node:fs';
const response = await client.images.generations.create({
model: 'x/z-image-turbo',
prompt: 'A red London double-decker bus at sunset',
size: '1024x1024',
});
const b64 = response.data[0].b64_json;
if (b64) {
writeFileSync('bus.png', Buffer.from(b64, 'base64'));
} Image generation is non-streaming — the endpoint returns a single JSON response once the worker finishes.
Embeddings
Generate vector embeddings for retrieval, search, and RAG pipelines. OpenAI-compatible — pass a string or array of strings and get back a CreateEmbeddingResponse with one embedding per input.
const response = await client.embeddings.create({
model: 'nomic-embed-text:latest',
input: ['The quick brown fox', 'jumps over the lazy dog'],
});
for (const item of response.data) {
console.log(item.index, (item.embedding as number[]).length, 'dims');
}
console.log(response.usage.prompt_tokens); Any embedding model in the Pendra model catalogue works — nomic-embed-text, mxbai-embed-large, bge-m3, qwen3-embedding, all-minilm.
List Models
const models = await client.models.list();
models.forEach((m) => console.log(m.id)); Migrating from OpenAI
The Pendra SDK mirrors the OpenAI interface. Two lines to switch — your existing code just works.
// Before
import OpenAI from 'openai';
const client = new OpenAI({ apiKey: 'sk-...' });
// After
import Pendra from 'pendra';
const client = new Pendra({ apiKey: 'pdr_sk_...' }); API Reference
client.chat.completions.create()
Create a chat completion. Returns ChatCompletion or Stream.
| Parameter | Type | Description |
|---|---|---|
| model | string | Model ID (e.g. "llama3.2") |
| messages | Array | Chat messages with role and content |
| stream | boolean? | Enable streaming (default false) |
| temperature | number? | Sampling temperature (0–2) |
| max_tokens | number? | Maximum tokens to generate |
| top_p | number? | Top-p sampling value |
| stop | string | string[]? | Stop sequence(s) |
client.images.generations.create()
Generate images from a text prompt. Returns Promise<ImageResponse>.
| Parameter | Type | Description |
|---|---|---|
| model | string | Image model ID (e.g. "x/z-image-turbo") |
| prompt | string | Text description of the image to generate |
| n | number? | Number of images, 1–4 (default 1) |
| size | string? | Dimensions as WIDTHxHEIGHT (default "1024x1024") |
| response_format | string? | "b64_json" (default) or "url" |
| num_inference_steps | number? | Diffusion steps (model-dependent) |
| seed | number? | Random seed for reproducibility |
| negative_prompt | string? | Text to avoid in the generated image |
client.embeddings.create()
Create embeddings. Returns Promise<CreateEmbeddingResponse>.
| Parameter | Type | Description |
|---|---|---|
| model | string | Embedding model ID (e.g. "nomic-embed-text:latest") |
| input | string | string[] | Text to embed. Accepts a single string or a batch. |
| encoding_format | "float" | "base64"? | Defaults to float |
| dimensions | number? | Output dimensionality (Matryoshka models like nomic-embed-text) |
| user | string? | Optional end-user identifier |
client.models.list()
Returns an array of Model objects. Each model has id, object, created, and owned_by fields.
Configuration
| Option | Env var | Default |
|---|---|---|
| apiKey | PENDRA_API_KEY | — |
| baseURL | — | https://api.pendra.ai |
| timeout | — | 120000 (ms) |
Error Handling
All exceptions extend APIError.
| Exception | Status | When |
|---|---|---|
| AuthenticationError | 401 | Invalid or missing API key |
| RateLimitError | 429 | Too many requests |
| APIStatusError | 4xx/5xx | Any other non-2xx response |
| APIConnectionError | — | Network or connection failure |