Every model provider.
One agent runtime, not eight.
samai-sdk gives you a real agent runtime — tool loop, handoffs, guardrails, MCP, RAG, structured
output, sessions, resumable runs, and full tracing — written once against a single Provider
interface. Swap OpenAI for Claude, or drop in a local Ollama model, by changing one line, not your whole app.
Eight providers converge on one Provider interface — the whole runtime is built against it, not against any one vendor.
Not a wrapper. Not a framework. The primitives, done properly.
Everything a production agent needs, implemented directly against generate()/stream() — so it works identically no matter which provider is behind it.
Agent runtime & handoffs
defineAgent() + runAgentStream() — the real tool loop, multi-agent handoffs with cycle prevention, and a full RunTrace on every run.
MCP client
createMCPClient() turns any MCP server's tools into ordinary ToolDefinitions — local stdio or remote HTTP/SSE.
Sandboxed code execution
createSandbox() runs real JS/Python/bash in an isolated temp dir — minimal env, real timeouts, byte-accurate output caps. createSandboxTools() wires it in as agent tools.
Voice / realtime agents
createRealtimeSession() — streamed audio/text over WebSocket, server VAD, one-call barge-in interrupt(), plus generateSpeech()/transcribeAudio().
Guardrails & approval
PII redaction, prompt-injection detection, budget caps, and human-in-the-loop approval gates that fail closed by default.
RAG / vector search
Swappable VectorStore + EmbeddingProvider, wired into a ready-to-use retrieval tool in one call.
Retries, fallback & timeouts
Real AbortController deadlines, exponential backoff, and provider fallback chains — every event fully traced.
Tracing, OTel & a local trace viewer
Every run produces a structured RunTrace. Export it to real OpenTelemetry spans, or render it as an offline HTML timeline with samai-sdk trace.
Resumable runs
Checkpoint after every turn and resume after a crash — no tool call is ever re-executed.
CLI & framework hooks
npx samai-sdk create scaffolds a runnable project. useAgent() ships for React, Vue and Svelte.
The rest of the toolkit
Every one of these ships in the same package as the runtime above — no separate install, no separate mental model.
Real web search
createWebSearchTool() — a live Tavily/Brave-backed tool, not a stub.
Structured output
generateObject(), streamObject() and batch, with auto-repair on failed validation.
Standard Schema support
Pass a zod schema or a valibot 1.x (or any Standard Schema) validator, interchangeably.
Prompt caching
promptCaching: true adds Anthropic cache breakpoints and surfaces cache-token usage.
Sessions & memory
In-memory, file, Redis, or SQLite-backed conversation history — one interface, four stores.
Usage & cost tracking
createUsageLedger() attributes token spend per session, user, or model.
Concurrency & rate limits
withConcurrencyLimit() / withRateLimit() queue calls instead of rejecting them.
Testing utilities
createMockProvider() scripts responses with a call log — no API key needed.
Multi-modal input
Image inputs — base64 or URL — correctly branched per provider's real API shape.
Typed error handling
AgentRunError, GuardrailBlockedError and more — every failure mode is a real type.
Edge & serverless ready
A deployment guide covering Node, Vercel Edge, and Cloudflare Workers, with a custom-store recipe.
Eight adapters. Same generate() / stream() shape.
Nothing in the agent runtime, guardrails, or tracing knows or cares which one is behind a given agent.
Six things you'll actually reach for
The same patterns from the docs, condensed — click through to the full reference for everything else.
import { createClient, anthropic, defineTool } from "samai-sdk"; import { z } from "zod"; const getWeather = defineTool({ name: "get_weather", parameters: z.object({ city: z.string() }), execute: async ({ city }) => ({ city, tempC: 28, condition: "sunny" }), }); const client = createClient({ provider: anthropic({ apiKey }) }); const result = await client.generate({ model: "claude-sonnet-4-6", messages: [{ role: "user", content: "Weather in Chennai?" }], tools: [getWeather], });
const packingAgent = defineAgent({ name: "packing_specialist", instructions: "Give packing advice from the weather already discussed.", model: "claude-sonnet-4-6", }); const routerAgent = defineAgent({ name: "trip_router", instructions: "Look up weather, then hand off to packing_specialist.", model: "claude-sonnet-4-6", tools: [getWeather], handoffs: [packingAgent], }); const result = await runAgent(client, routerAgent, "Pack for Tokyo?"); result.finalAgent; // "packing_specialist"
const filesystem = createMCPClient({ transport: { transport: "stdio", command: "npx", args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"], }, toolPrefix: "fs", }); const agent = defineAgent({ name: "file_assistant", instructions: "Help inspect files in /tmp using the fs__ tools.", model: "claude-sonnet-4-6", tools: await filesystem.tools(), });
const sandbox = createSandbox(); const agent = defineAgent({ name: "coder", instructions: "Write and test code using write_file and execute_code.", model: "claude-sonnet-4-6", tools: createSandboxTools(sandbox), // execute_code, write_file, read_file, list_files }); const result = await runAgent(client, agent, "Write fibonacci.py, run it, and tell me the output."); await sandbox.close(); // deletes the temp directory
const session = createRealtimeSession({ instructions: "You are a helpful, concise voice assistant.", voice: "alloy", tools: [getWeatherTool], // called automatically when the model invokes them }); session.on((event) => { if (event.type === "audio.delta") playAudioChunk(event.audio); if (event.type === "speech_started") stopSpeakerPlayback(); // barge-in }); await session.connect(); session.sendText("What's the weather in Tokyo?"); session.interrupt(); // cancels the in-flight response, instantly
const client = createClient({ provider: openai({ apiKey }), inputGuardrails: [ createPiiInputGuardrail({ mode: "redact" }), createPromptInjectionGuardrail({ mode: "block" }), ], outputGuardrails: [ createSchemaGuardrail(ReviewSchema), ], }); // blocked calls throw GuardrailBlockedError with the reason attached
Between "you build the loop yourself" and vendor lock-in
Thin provider wrapper
One API across providers, but no orchestration — you still write the tool loop, handoffs, retries, and tracing yourself.
samai-sdk
A full agent runtime — loop, handoffs, guardrails, sessions, tracing, resumable runs — written against generate()/stream(), so it stays provider-agnostic by construction. Under 2,500 lines, readable in an afternoon.
Heavyweight framework
Full orchestration, but its own state and deployment model — and it usually shines brightest on one vendor's models.
Scaffold a working agent in one command
No manual wiring — pick a provider, get a runnable project with one agent and one tool already working.