badgr-llm-guard
v0.1.0
Published
Thin guarded AI API client with retries, Retry-After support, spend caps, request IDs, and receipts.
Downloads
38
Maintainers
Readme
badgr-llm-guard
Wrap any AI API call with automatic retries, spend caps, timeouts, and request receipts — so you never get a silent failure or a surprise bill.
import { withGuard } from "badgr-llm-guard";
const guardedCall = withGuard({ maxRetries: 3, timeoutMs: 30_000, maxSpendUsd: 5 });
const result = await guardedCall(() => openai.chat.completions.create(request));Free. No signup required. Works with any AI provider.
The problem it solves
AI API calls fail in frustrating ways: rate limits with no backoff, silent timeouts that hang forever, and costs that spiral when something loops. badgr-llm-guard adds a safety layer around any AI call — automatic Retry-After handling, hard spend caps, and receipts that tell you exactly what was spent.
Quick start
npm install badgr-llm-guardimport { withGuard } from "badgr-llm-guard";
import OpenAI from "openai";
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
const guardedCall = withGuard({
maxRetries: 3, // retry on 429, 408, and 5xx
timeoutMs: 30_000, // abort if no response after 30s
maxSpendUsd: 5, // throw if cumulative spend exceeds $5
});
const result = await guardedCall(() =>
openai.chat.completions.create({
model: "gpt-4o",
messages: [{ role: "user", content: "Summarize this document..." }],
})
);Options
withGuard({
maxRetries?: number; // How many times to retry on 429/408/5xx (default: 2)
timeoutMs?: number; // Abort after this many ms (default: 30_000)
maxSpendUsd?: number; // Throw if cumulative spend exceeds this amount
receipt?: "terminal" // Print receipt to terminal after each call (default)
| "json" // Return receipt as JSON
| "none"; // Suppress receipt output
estimateCostUsd?: (attempt: number) => number; // Custom cost estimator
})Retry behaviour
| HTTP status | Action |
|---|---|
| 429 | Read Retry-After header, wait, then retry |
| 408 | Retry immediately |
| 5xx | Retry with exponential backoff |
| Other errors | Do not retry — throw immediately |
Spend cap
The guard reads cost from x-badgr-cost-usd or x-cost-usd response headers. If cumulative spend plus the estimated next call would exceed maxSpendUsd, the call is blocked and an error is thrown — before the request is made.
const guardedCall = withGuard({ maxSpendUsd: 1 });
try {
await guardedCall(() => openai.chat.completions.create(...)); // $0.40
await guardedCall(() => openai.chat.completions.create(...)); // $0.40
await guardedCall(() => openai.chat.completions.create(...)); // throws — would exceed $1
} catch (e) {
console.log(e.message); // "Spend cap exceeded: $0.80 spent, $1.00 limit"
}Receipts
Every call produces a receipt:
badgr-llm-guard receipt
request-id req_7f3a9b2c
attempts 2 (1 retry after 429)
status 200
cost $0.004
total spent $0.008CLI — check which API keys are configured
npx badgr-llm-guard check
# ✓ OPENAI_API_KEY set
# ✓ ANTHROPIC_API_KEY set
# ✗ GEMINI_API_KEY not set
# ✗ BADGR_API_KEY not set
npx badgr-llm-guard demo # show a withGuard usage exampleTypeScript API
import { withGuard, createGuardedClient } from "badgr-llm-guard";
// Wrap any async function
const guardedCall = withGuard({ maxRetries: 2, timeoutMs: 30_000 });
const result = await guardedCall(() => myAiSdkCall());
console.log(guardedCall.getSpentUsd()); // total spend so far
console.log(guardedCall.getLastReport()); // full JSON report
// Low-level HTTP client (for providers without an SDK)
const client = createGuardedClient({
apiKey: process.env.BADGR_API_KEY,
baseUrl: "https://api.aibadgr.com/v1",
maxRetries: 3,
timeoutMs: 30_000,
maxSpendUsd: 10,
});
const response = await client.request("/chat/completions", {
method: "POST",
body: JSON.stringify({ model: "...", messages: [...] }),
});Optional: AI Badgr provider fallback
If local retries are exhausted, route to AI Badgr as a fallback provider:
import OpenAI from "openai";
const openai = new OpenAI({
apiKey: process.env.BADGR_API_KEY,
baseURL: "https://api.aibadgr.com/v1", // drop-in OpenAI-compatible endpoint
});
const guardedCall = withGuard({ maxRetries: 3 });
const result = await guardedCall(() => openai.chat.completions.create(request));Requirements
- Node.js 18+
