weysabi
v0.15.1
Published
AI orchestration for fullstack devs — provider failover, structured output, RAG, and streaming. One library, zero markup.
Downloads
33
Maintainers
Readme
weysabi
AI orchestration for fullstack devs. Provider failover, structured output, streaming, RAG, guardrails, and prompt management — one dependency, zero markup.
import { createWeysabi } from "weysabi";
import { z } from "zod";
const weysabi = createWeysabi({
groq: { apiKey: process.env.GROQ_API_KEY },
openai: { apiKey: process.env.OPENAI_API_KEY },
});
const res = await weysabi.complete({
model: "groq/llama-4-scout",
messages: [{ role: "user", content: "Extract the invoice total." }],
fallbacks: ["openai/gpt-4o-mini"],
response: { schema: z.object({ total: z.number() }) },
rag: ["invoices/*.pdf"],
guardrails: ["pii"],
});
console.log(res.parsed.total, res.latencyMs, res.requestId);Why Weysabi?
- Your keys, your providers. No gateway, no token markup, no middleman.
- One dependency. Not LangChain + provider SDKs + vector DB. Just
weysabi. - Built-in everything. Structured output, RAG, guardrails, prompts, streaming, caching — all ship in the box.
- Works offline-first. SQLite for conversations and RAG. No cloud dependency.
- No lock-in. Stop paying, the library still works. Your data stays with you.
Features
| Feature | Status |
| --------------------------------------------------------------------------------------------------------------- | ------ |
| Provider abstraction (OpenAI, Groq, Anthropic, Google, Mistral, DeepSeek, OpenRouter, Together, Nvidia, Ollama) | ✅ |
| Custom / OpenAI-compatible endpoints | ✅ |
| Custom provider handlers (register your own) | ✅ |
| Provider failover (primary → fallbacks) | ✅ |
| Circuit breaker + retry + backoff | ✅ |
| Streaming (SSE, async iterable) | ✅ |
| Structured output (Zod schemas with auto-retry) | ✅ |
| Tool calling (auto-execute + chaining) | ✅ |
| Prompt templates with {variable} substitution | ✅ |
| Prompt registry (register, render, run) | ✅ |
| RAG engine (ingest, embed, HNSW search, persist) | ✅ |
| Multi-project RAG manager | ✅ |
| Guardrails (PII redaction, injection detection, content safety, token limits) | ✅ |
| Conversation memory (SQLite/Postgres, auto-truncation) | ✅ |
| ChatSDK (prepare + call + record in one) | ✅ |
| Framework adapters (Hono, Next.js, Express, Fastify, Elysia) | ✅ |
| Caching (InMemory with LRU, Redis, BYO) | ✅ |
| Plugin system (lifecycle hooks) | ✅ |
| OpenTelemetry integration | ✅ |
| Vercel AI SDK adapter | ✅ |
| Cost estimation (per-response estimatedCostUsd) | ✅ |
| requestId tracking (auto-generated, all logs) | ✅ |
| Error status codes (programmatic retry vs. fatal) | ✅ |
| WebSocket client (real-time streaming) | ✅ |
| CLI (weysabi init, create, complete, stream, config, prompt) | ✅ |
| Control plane — projects, conversations, runs, API keys | 🔜 |
| Weysabi Cloud — hosted control plane, evals, monitoring | 🔜 |
Install
bun add weysabiRequires Bun ≥ 1.3.
Providers
Configure any provider with an API key. All providers share the same interface.
const weysabi = createWeysabi({
openai: { apiKey: process.env.OPENAI_API_KEY },
anthropic: { apiKey: process.env.ANTHROPIC_API_KEY },
google: { apiKey: process.env.GOOGLE_API_KEY },
groq: { apiKey: process.env.GROQ_API_KEY },
deepseek: { apiKey: process.env.DEEPSEEK_API_KEY },
mistral: { apiKey: process.env.MISTRAL_API_KEY },
nvidia: { apiKey: process.env.NVIDIA_API_KEY },
openrouter: { apiKey: process.env.OPENROUTER_API_KEY },
together: { apiKey: process.env.TOGETHER_API_KEY },
ollama: { baseUrl: "http://localhost:11434" },
// Custom OpenAI-compatible endpoint
myproxy: { apiKey: "sk-...", baseUrl: "https://my-proxy.com/v1" },
});Reference models with provider/model-id notation:
weysabi.complete({ model: "groq/llama-4-scout", ... });
weysabi.complete({ model: "openai/gpt-4o", ... });
weysabi.complete({ model: "anthropic/claude-3-5-sonnet-20241022", ... });Custom Provider Handlers
For providers with non-OpenAI-compatible APIs, register a custom handler:
import { registerProvider } from "weysabi";
import type { ProviderHandler } from "weysabi";
const myHandler: ProviderHandler = {
buildUrl(baseUrl, modelId, stream) {
/* ... */
},
buildHeaders(apiKey) {
/* ... */
},
buildBody(modelId, messages, opts) {
/* ... */
},
parseResponse(data) {
/* ... */
},
parseStreamChunk(data) {
/* ... */
},
};
registerProvider("my-custom-provider", myHandler);
const weysabi = createWeysabi({
"my-custom-provider": { apiKey: process.env.MY_KEY },
});Provider Failover
Automatic fallback when a provider fails. Circuit breaker prevents hammering a failing endpoint.
const res = await weysabi.complete({
model: "groq/llama-4-scout",
fallbacks: ["openai/gpt-4o-mini", "anthropic/claude-3-5-haiku-20241022"],
messages: [{ role: "user", content: "Hello" }],
});
// Groq fails → OpenAI fallback → response deliveredStructured Output
Pass a Zod schema. Weysabi validates the response and retries on parse failure.
import { z } from "zod";
const CalendarSchema = z.object({
events: z.array(
z.object({
title: z.string(),
date: z.string(),
attendees: z.array(z.string()),
})
),
});
const res = await weysabi.complete({
model: "groq/llama-4-scout",
messages: [{ role: "user", content: "Schedule a meeting for next Tuesday." }],
schema: CalendarSchema,
});
console.log(res.parsed.events);
// Typed as { title: string; date: string; attendees: string[] }[]On parse failure, throws SchemaValidationError with .raw (raw response) and .issues (Zod errors). Each error carries a statusCode property for programmatic handling.
Streaming
Works with all providers and includes failover — if the primary provider fails mid-stream, the library transparently retries the full request against fallbacks.
const stream = weysabi.stream({
model: "groq/llama-4-scout",
messages: [{ role: "user", content: "Write a poem." }],
});
for await (const chunk of stream) {
if (chunk.content) process.stdout.write(chunk.content);
if (chunk.usage) console.log("\nTokens:", chunk.usage.totalTokens);
}Framework Adapters
// Hono, Next.js, Elysia — any Web Fetch framework
import { toResponse } from "weysabi/hono";
// import { toResponse } from "weysabi/next";
// import { toResponse } from "weysabi/elysia";
app.post("/chat", async (c) => {
const stream = weysabi.stream({ ... });
return toResponse(stream); // SSE response
});
// Express
import { pipe } from "weysabi/express";
app.post("/chat", async (req, res) => {
const stream = weysabi.stream({ ... });
await pipe(stream, res);
});
// Fastify
import { pipe } from "weysabi/fastify";
app.post("/chat", async (req, reply) => {
const stream = weysabi.stream({ ... });
await pipe(stream, reply);
});Batch Requests
Send multiple completions concurrently with a configurable concurrency limit.
const results = await weysabi.batch(
[
{ model: "groq/llama-4-scout", messages: [{ role: "user", content: "Q1" }] },
{ model: "groq/llama-4-scout", messages: [{ role: "user", content: "Q2" }] },
{ model: "groq/llama-4-scout", messages: [{ role: "user", content: "Q3" }] },
],
{ concurrency: 5 }
);
for (const result of results) {
if (result.ok) {
console.log(result.data.content);
} else {
console.error("Failed:", result.error);
}
}Results are returned in request order. Errors are isolated per request.
Prompt Templates
Define, register, and run templates with {variable} substitution.
weysabi.prompts.register({
id: "classify",
model: "groq/llama-4-scout",
messages: [
{
role: "system",
content: "You are a support ticket classifier.",
},
{
role: "user",
content: `Classify this ticket: {ticket_text}
Categories: billing, technical, account, feature_request
Respond with just the category.`,
},
],
});
// Render + complete in one call
const result = await weysabi.prompts.run("classify", {
ticket_text: "I was overcharged for my subscription",
});
// Or render separately for inspection
const messages = weysabi.prompts.render("classify", {
ticket_text: "Login is broken",
});RAG (Retrieval-Augmented Generation)
Built-in vector search without an external vector database. Ingest documents, auto-chunk, embed, and search.
import { RagEngine } from "weysabi/rag";
const rag = new RagEngine({
dbPath: ".weysabi/knowledge.db",
embeddingModel: "openai/text-embedding-3-small",
});
rag.setProviders(
{ provider: "openai", apiKey: process.env.OPENAI_API_KEY },
{ openai: { apiKey: process.env.OPENAI_API_KEY } }
);
// Ingest files
await rag.load("docs/manual.pdf", "docs/faq.md");
// Query
const results = await rag.query("How do I reset my password?");
console.log(results[0].content);Multi-project management:
import { RagManager } from "weysabi/rag";
const manager = new RagManager({
basePath: ".weysabi/rag",
providers: { embeddingProvider: { provider: "openai", apiKey: "..." } },
});
const docs = manager.project("docs-v2");
await docs.load("guides/");
const hits = await docs.query("pricing");Guardrails
PII redaction, prompt injection detection, content moderation, and output token limits.
const res = await weysabi.complete({
model: "groq/llama-4-scout",
messages: [{ role: "user", content: "My email is [email protected]" }],
guardrails: ["pii"], // Redact emails, phones, SSNs
// guardrails: ["injection"], // Detect prompt injection
// guardrails: ["content"], // Moderate toxic content
// guardrails: [{ type: "limits", maxOutputTokens: 100 }],
});Guardrails can also be used standalone:
import { guardrail } from "weysabi/guardrails";
const result = await guardrail("pii", "My SSN is 123-45-6789");
console.log(result.redacted); // "My SSN is [REDACTED]"Custom Guardrails
weysabi.guardrail("my-check", {
scope: "output",
validate: (text) => text.length < 1000,
onViolation: (match) => console.log("Blocked:", match),
});Conversation Memory
Persistent chat history with automatic context management. SQLite by default, Postgres for production.
import { ConversationMemory } from "weysabi/chat";
const memory = new ConversationMemory({
dbPath: ".weysabi/chat.db",
maxHistoryTokens: 16384,
});
// Prepare context — no API call
const ctx = memory.prepare("user-abc", {
message: "Hi, my name is Bob",
system: "You are a helpful assistant",
});
// Call your provider SDK natively
const response = await anthropic.messages.create({
model: "claude-3-5-sonnet-20241022",
system: ctx.system,
messages: ctx.messages,
});
// Record the turn
memory.record("user-abc", {
userMessage: { content: "Hi, my name is Bob" },
assistantMessage: { content: response.content[0].text },
});
// Next turn — history is loaded automatically
const ctx2 = memory.prepare("user-abc", {
message: "What's my name?",
});
// ctx2.messages includes full historyPostgres for production:
import postgres from "postgres";
import { ConversationMemory, PgSessionStore } from "weysabi/chat";
const sql = postgres("postgres://user:pass@host:5432/db");
const memory = new ConversationMemory({
store: new PgSessionStore(sql),
});Tool Calling
Define tools with Zod schemas. The library handles execution and chaining.
const res = await weysabi.complete({
model: "groq/llama-4-scout",
messages: [{ role: "user", content: "What's the weather in London?" }],
tools: [
{
name: "get_weather",
description: "Get current weather",
schema: z.object({ city: z.string() }),
execute: async ({ city }) => fetchWeather(city),
},
],
});Caching
import { InMemoryCache } from "weysabi/cache";
const weysabi = createWeysabi(providers, {
cache: new InMemoryCache(60_000), // 60s TTL
});InMemoryCache supports max-size LRU eviction — pass a second argument to prevent unbounded growth:
const cache = new InMemoryCache(60_000, 1000); // 1000 entries maxrequestId Tracking
Every request gets a unique requestId, auto-generated via crypto.getRandomValues(). Pass your own for correlation:
const res = await weysabi.complete({
model: "groq/llama-4-scout",
messages: [{ role: "user", content: "Hello" }],
requestId: "my-correlation-id",
});
console.log(res.requestId); // "my-correlation-id"The requestId flows through all structured log entries, making debugging across services straightforward.
Error Handling
Every error class carries a statusCode for programmatic retry vs. fatal distinction:
| Error | statusCode | Meaning |
| ---------------------------- | ---------- | ---------------------- |
| SchemaValidationError | 422 | Invalid user input |
| ProviderNotConfiguredError | 400 | Provider not in config |
| PromptNotFoundError | 404 | Template missing |
| MissingPromptInputError | 400 | Missing variable |
| MaxToolCallsExceededError | 400 | Tool loop limit |
| CircuitBreakerOpenError | 503 | Provider recovering |
| AllModelsFailedError | 502 | All providers failed |
| ProviderRequestError | varies | Upstream HTTP code |
| WeysabiError (base) | 500 | Unknown error |
try {
const res = await weysabi.complete({ ... });
} catch (err) {
if (err instanceof WeysabiError) {
if (err.statusCode < 500) {
console.log("Client error, fix the request:", err.message);
} else {
console.log("Server error, can retry:", err.statusCode);
}
}
}Plugins
Lifecycle hooks for telemetry, logging, or custom behavior.
weysabi.use({
name: "logger",
onCompleteRequest(req) {
console.log("Request:", req.model);
return req;
},
onCompleteResponse(res, req) {
console.log("Response:", res.latencyMs, "ms");
return res;
},
onError(err, { request }) {
console.error("Failed:", err.message);
},
});OpenTelemetry
import { createOtelPlugin } from "weysabi/otel";
weysabi.use(createOtelPlugin({ tracer: trace.getTracer("my-app") }));Vercel AI SDK
import { createWeysabiProvider } from "weysabi/ai-sdk";
const provider = createWeysabiProvider(weysabi);
const result = await generateText({
model: provider.languageModel("groq/llama-4-scout"),
prompt: "Hello",
});Control Plane Client
Built-in HTTP + WebSocket client for the Weysabi server control plane — projects, conversations, runs, prompts, documents, and API keys.
import { createWeysabiClient, createWebSocketClient } from "weysabi";
// HTTP client
const client = createWeysabiClient({
baseUrl: "http://localhost:3000",
apiKey: "sk-admin",
});
const project = client.project("project-1");
const runs = await project.runs.list({ status: "success" });
// WebSocket streaming
const ws = createWebSocketClient({
baseUrl: "http://localhost:3000",
apiKey: "sk-admin",
});
const events = ws.stream("POST", "/v1/projects/p/conversations/c/messages/stream", {
content: "Hello",
model: "groq/llama-4-scout",
});CLI
# Scaffold a new project
bunx create-weysabi-app my-app
# Interactive project setup
bun weysabi init
# Test provider connectivity
bun weysabi config validate
# Quick completions
bun weysabi complete "Hello" -m groq/llama-4-scout
bun weysabi stream "Tell me a story" -m openai/gpt-4o-mini
# Manage prompts
bun weysabi prompt list
bun weysabi prompt add classify -f prompts/classify.txtHTTP Server
Deploy as an OpenAI-compatible API with auth, rate limits, quotas, usage tracking, and admin monitoring.
bun weysabi server --port 3000Or embed in your app:
import { createServer } from "weysabi-server";
const server = await createServer(weysabi, {
apiKey: "sk-my-key",
adminApiKey: "sk-admin-secret",
});See weysabi-server for full documentation.
Sub-path Exports
import { createWeysabi } from "weysabi";
import { WeysabiError } from "weysabi/errors";
import { toResponse } from "weysabi/sse";
import { pipe } from "weysabi/express";
import { InMemoryCache } from "weysabi/cache";
import { createOtelPlugin } from "weysabi/otel";
import { createWeysabiProvider } from "weysabi/ai-sdk";
import { RagEngine, RagManager } from "weysabi/rag";
import { ConversationMemory } from "weysabi/chat";
import { PromptRegistry } from "weysabi/prompts";
import { registerProvider } from "weysabi";
import type { ProviderHandler } from "weysabi";
import { WebSocketClient, createWebSocketClient } from "weysabi/client";License
MIT
