badgr-llm-guard 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +171 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +101 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +196 -0
- package/dist/index.js.map +1 -0
- package/package.json +16 -0
package/README.md
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# badgr-llm-guard
|
|
2
|
+
|
|
3
|
+
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.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { withGuard } from "badgr-llm-guard";
|
|
7
|
+
|
|
8
|
+
const guardedCall = withGuard({ maxRetries: 3, timeoutMs: 30_000, maxSpendUsd: 5 });
|
|
9
|
+
const result = await guardedCall(() => openai.chat.completions.create(request));
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
**Free. No signup required.** Works with any AI provider.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## The problem it solves
|
|
17
|
+
|
|
18
|
+
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.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Quick start
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install badgr-llm-guard
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
import { withGuard } from "badgr-llm-guard";
|
|
30
|
+
import OpenAI from "openai";
|
|
31
|
+
|
|
32
|
+
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
|
|
33
|
+
|
|
34
|
+
const guardedCall = withGuard({
|
|
35
|
+
maxRetries: 3, // retry on 429, 408, and 5xx
|
|
36
|
+
timeoutMs: 30_000, // abort if no response after 30s
|
|
37
|
+
maxSpendUsd: 5, // throw if cumulative spend exceeds $5
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const result = await guardedCall(() =>
|
|
41
|
+
openai.chat.completions.create({
|
|
42
|
+
model: "gpt-4o",
|
|
43
|
+
messages: [{ role: "user", content: "Summarize this document..." }],
|
|
44
|
+
})
|
|
45
|
+
);
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Options
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
withGuard({
|
|
54
|
+
maxRetries?: number; // How many times to retry on 429/408/5xx (default: 2)
|
|
55
|
+
timeoutMs?: number; // Abort after this many ms (default: 30_000)
|
|
56
|
+
maxSpendUsd?: number; // Throw if cumulative spend exceeds this amount
|
|
57
|
+
receipt?: "terminal" // Print receipt to terminal after each call (default)
|
|
58
|
+
| "json" // Return receipt as JSON
|
|
59
|
+
| "none"; // Suppress receipt output
|
|
60
|
+
estimateCostUsd?: (attempt: number) => number; // Custom cost estimator
|
|
61
|
+
})
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Retry behaviour
|
|
67
|
+
|
|
68
|
+
| HTTP status | Action |
|
|
69
|
+
|---|---|
|
|
70
|
+
| `429` | Read `Retry-After` header, wait, then retry |
|
|
71
|
+
| `408` | Retry immediately |
|
|
72
|
+
| `5xx` | Retry with exponential backoff |
|
|
73
|
+
| Other errors | Do not retry — throw immediately |
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Spend cap
|
|
78
|
+
|
|
79
|
+
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.
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
const guardedCall = withGuard({ maxSpendUsd: 1 });
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
await guardedCall(() => openai.chat.completions.create(...)); // $0.40
|
|
86
|
+
await guardedCall(() => openai.chat.completions.create(...)); // $0.40
|
|
87
|
+
await guardedCall(() => openai.chat.completions.create(...)); // throws — would exceed $1
|
|
88
|
+
} catch (e) {
|
|
89
|
+
console.log(e.message); // "Spend cap exceeded: $0.80 spent, $1.00 limit"
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Receipts
|
|
96
|
+
|
|
97
|
+
Every call produces a receipt:
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
badgr-llm-guard receipt
|
|
101
|
+
request-id req_7f3a9b2c
|
|
102
|
+
attempts 2 (1 retry after 429)
|
|
103
|
+
status 200
|
|
104
|
+
cost $0.004
|
|
105
|
+
total spent $0.008
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## CLI — check which API keys are configured
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
npx badgr-llm-guard check
|
|
114
|
+
# ✓ OPENAI_API_KEY set
|
|
115
|
+
# ✓ ANTHROPIC_API_KEY set
|
|
116
|
+
# ✗ GEMINI_API_KEY not set
|
|
117
|
+
# ✗ BADGR_API_KEY not set
|
|
118
|
+
|
|
119
|
+
npx badgr-llm-guard demo # show a withGuard usage example
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## TypeScript API
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
import { withGuard, createGuardedClient } from "badgr-llm-guard";
|
|
128
|
+
|
|
129
|
+
// Wrap any async function
|
|
130
|
+
const guardedCall = withGuard({ maxRetries: 2, timeoutMs: 30_000 });
|
|
131
|
+
const result = await guardedCall(() => myAiSdkCall());
|
|
132
|
+
console.log(guardedCall.getSpentUsd()); // total spend so far
|
|
133
|
+
console.log(guardedCall.getLastReport()); // full JSON report
|
|
134
|
+
|
|
135
|
+
// Low-level HTTP client (for providers without an SDK)
|
|
136
|
+
const client = createGuardedClient({
|
|
137
|
+
apiKey: process.env.BADGR_API_KEY,
|
|
138
|
+
baseUrl: "https://api.aibadgr.com/v1",
|
|
139
|
+
maxRetries: 3,
|
|
140
|
+
timeoutMs: 30_000,
|
|
141
|
+
maxSpendUsd: 10,
|
|
142
|
+
});
|
|
143
|
+
const response = await client.request("/chat/completions", {
|
|
144
|
+
method: "POST",
|
|
145
|
+
body: JSON.stringify({ model: "...", messages: [...] }),
|
|
146
|
+
});
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Optional: AI Badgr provider fallback
|
|
152
|
+
|
|
153
|
+
If local retries are exhausted, route to AI Badgr as a fallback provider:
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
import OpenAI from "openai";
|
|
157
|
+
|
|
158
|
+
const openai = new OpenAI({
|
|
159
|
+
apiKey: process.env.BADGR_API_KEY,
|
|
160
|
+
baseURL: "https://api.aibadgr.com/v1", // drop-in OpenAI-compatible endpoint
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
const guardedCall = withGuard({ maxRetries: 3 });
|
|
164
|
+
const result = await guardedCall(() => openai.chat.completions.create(request));
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## Requirements
|
|
170
|
+
|
|
171
|
+
- Node.js 18+
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createLogger, fireTelemetry } from "badgr-shared";
|
|
3
|
+
import { withGuard } from "./index.js";
|
|
4
|
+
fireTelemetry({ package: "badgr-llm-guard", command: process.argv[2] });
|
|
5
|
+
function readArg(name) {
|
|
6
|
+
const index = process.argv.indexOf(name);
|
|
7
|
+
return index >= 0 ? process.argv[index + 1] : undefined;
|
|
8
|
+
}
|
|
9
|
+
const command = process.argv[2];
|
|
10
|
+
const json = process.argv.includes("--json");
|
|
11
|
+
const logger = createLogger(json);
|
|
12
|
+
if (!command || command === "--help" || command === "-h") {
|
|
13
|
+
console.log(`badgr-llm-guard — local retry, spend cap, timeout, and receipt wrapper for LLM calls
|
|
14
|
+
|
|
15
|
+
Usage:
|
|
16
|
+
badgr-llm-guard check Check which LLM API keys are configured
|
|
17
|
+
badgr-llm-guard demo Show withGuard usage example
|
|
18
|
+
badgr-llm-guard --help Show this help
|
|
19
|
+
|
|
20
|
+
Flags:
|
|
21
|
+
--json Output machine-readable JSON
|
|
22
|
+
|
|
23
|
+
Library usage (no CLI needed):
|
|
24
|
+
import { withGuard, createGuardedClient } from "badgr-llm-guard";
|
|
25
|
+
|
|
26
|
+
const guarded = withGuard({ maxRetries: 3, timeoutMs: 30_000, maxSpendUsd: 2 });
|
|
27
|
+
const result = await guarded(() => openai.chat.completions.create(request));
|
|
28
|
+
|
|
29
|
+
Environment:
|
|
30
|
+
BADGR_TELEMETRY=0 Disable anonymous usage telemetry`);
|
|
31
|
+
process.exit(0);
|
|
32
|
+
}
|
|
33
|
+
if (command === "check") {
|
|
34
|
+
const providers = {
|
|
35
|
+
"OPENAI_API_KEY": Boolean(process.env.OPENAI_API_KEY),
|
|
36
|
+
"ANTHROPIC_API_KEY": Boolean(process.env.ANTHROPIC_API_KEY),
|
|
37
|
+
"GEMINI_API_KEY": Boolean(process.env.GEMINI_API_KEY),
|
|
38
|
+
"OPENROUTER_API_KEY": Boolean(process.env.OPENROUTER_API_KEY),
|
|
39
|
+
"BADGR_API_KEY": Boolean(process.env.BADGR_API_KEY),
|
|
40
|
+
};
|
|
41
|
+
const configured = Object.entries(providers).filter(([, v]) => v).map(([k]) => k);
|
|
42
|
+
const missing = Object.entries(providers).filter(([, v]) => !v).map(([k]) => k);
|
|
43
|
+
if (json) {
|
|
44
|
+
console.log(JSON.stringify({
|
|
45
|
+
tool: "badgr-llm-guard",
|
|
46
|
+
status: configured.length > 0 ? "passed" : "warning",
|
|
47
|
+
summary: configured.length > 0
|
|
48
|
+
? `${configured.length} provider key(s) configured`
|
|
49
|
+
: "No LLM provider keys found in environment",
|
|
50
|
+
configured,
|
|
51
|
+
missing,
|
|
52
|
+
generatedAt: new Date().toISOString(),
|
|
53
|
+
}, null, 2));
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
logger.line("badgr-llm-guard — provider key check");
|
|
57
|
+
logger.line("");
|
|
58
|
+
for (const [key, present] of Object.entries(providers)) {
|
|
59
|
+
logger.line(`${present ? "✓" : "✗"} ${key}${present ? " (set)" : " (missing)"}`);
|
|
60
|
+
}
|
|
61
|
+
logger.line("");
|
|
62
|
+
if (configured.length > 0) {
|
|
63
|
+
logger.line(`${configured.length} provider(s) ready for guarded calls.`);
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
logger.line("No LLM API keys detected. Set one of the keys above before using withGuard.");
|
|
67
|
+
}
|
|
68
|
+
logger.line("");
|
|
69
|
+
logger.line("Optional: route calls through the AI Badgr gateway for multi-provider fallback:");
|
|
70
|
+
logger.line(" baseURL: https://api.aibadgr.com/v1");
|
|
71
|
+
}
|
|
72
|
+
process.exitCode = configured.length > 0 ? 0 : 1;
|
|
73
|
+
process.exit();
|
|
74
|
+
}
|
|
75
|
+
if (command === "demo") {
|
|
76
|
+
console.log(`// badgr-llm-guard — library usage demo
|
|
77
|
+
|
|
78
|
+
import { withGuard, createGuardedClient } from "badgr-llm-guard";
|
|
79
|
+
|
|
80
|
+
// Wrap any async SDK call:
|
|
81
|
+
const guarded = withGuard({ maxRetries: 3, timeoutMs: 30_000, maxSpendUsd: 2, receipt: "terminal" });
|
|
82
|
+
const result = await guarded(() => openai.chat.completions.create({ model: "gpt-4o", messages }));
|
|
83
|
+
|
|
84
|
+
// Or wrap a raw fetch client:
|
|
85
|
+
const client = createGuardedClient({ apiKey: process.env.OPENAI_API_KEY, maxRetries: 2, maxSpendUsd: 5 });
|
|
86
|
+
const res = await client.request("/chat/completions", { method: "POST", body: JSON.stringify(body) });
|
|
87
|
+
|
|
88
|
+
// Spend tracking:
|
|
89
|
+
console.log("spent:", client.getSpentUsd(), "USD");
|
|
90
|
+
|
|
91
|
+
// Optional AI Badgr gateway for multi-provider fallback (no signup required for local guarding):
|
|
92
|
+
// baseURL: "https://api.aibadgr.com/v1"`);
|
|
93
|
+
process.exit(0);
|
|
94
|
+
}
|
|
95
|
+
// Unrecognised command — show helpful error.
|
|
96
|
+
const guardedCheck = withGuard({ maxRetries: 0, timeoutMs: 1, receipt: "none" });
|
|
97
|
+
void guardedCheck;
|
|
98
|
+
console.error(`badgr-llm-guard: unknown command "${command}"`);
|
|
99
|
+
console.error("Run badgr-llm-guard --help for usage.");
|
|
100
|
+
process.exit(2);
|
|
101
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type JsonReport } from "badgr-shared";
|
|
2
|
+
export interface GuardedClientOptions {
|
|
3
|
+
apiKey?: string;
|
|
4
|
+
baseUrl?: string;
|
|
5
|
+
maxRetries?: number;
|
|
6
|
+
timeoutMs?: number;
|
|
7
|
+
maxSpendUsd?: number;
|
|
8
|
+
receipt?: "terminal" | "json" | "none";
|
|
9
|
+
fetchImpl?: typeof fetch;
|
|
10
|
+
}
|
|
11
|
+
export interface GuardOptions extends Omit<GuardedClientOptions, "apiKey" | "baseUrl" | "fetchImpl"> {
|
|
12
|
+
estimateCostUsd?: (attempt: number) => number;
|
|
13
|
+
}
|
|
14
|
+
export interface GuardedRequestInit extends RequestInit {
|
|
15
|
+
estimatedCostUsd?: number;
|
|
16
|
+
}
|
|
17
|
+
export interface GuardReceipt {
|
|
18
|
+
requestId: string;
|
|
19
|
+
attempts: number;
|
|
20
|
+
status: number | "failed" | "completed";
|
|
21
|
+
costUsd: number;
|
|
22
|
+
spentUsd: number;
|
|
23
|
+
}
|
|
24
|
+
export interface GuardedClient {
|
|
25
|
+
request(path: string, init?: GuardedRequestInit): Promise<Response>;
|
|
26
|
+
getSpentUsd(): number;
|
|
27
|
+
getLastReport(): JsonReport | undefined;
|
|
28
|
+
}
|
|
29
|
+
export type GuardedCall = (<T>(fn: () => Promise<T> | T) => Promise<T>) & {
|
|
30
|
+
getSpentUsd(): number;
|
|
31
|
+
getLastReport(): JsonReport | undefined;
|
|
32
|
+
};
|
|
33
|
+
export declare function withGuard(options: GuardOptions): GuardedCall;
|
|
34
|
+
export declare function createGuardedClient(options: GuardedClientOptions): GuardedClient;
|
|
35
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiD,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAE9F,MAAM,WAAW,oBAAoB;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,UAAU,GAAG,MAAM,GAAG,MAAM,CAAC;IACvC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AAED,MAAM,WAAW,YAAa,SAAQ,IAAI,CAAC,oBAAoB,EAAE,QAAQ,GAAG,SAAS,GAAG,WAAW,CAAC;IAClG,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;CAC/C;AAED,MAAM,WAAW,kBAAmB,SAAQ,WAAW;IACrD,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,QAAQ,GAAG,WAAW,CAAC;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IACpE,WAAW,IAAI,MAAM,CAAC;IACtB,aAAa,IAAI,UAAU,GAAG,SAAS,CAAC;CACzC;AAED,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG;IACxE,WAAW,IAAI,MAAM,CAAC;IACtB,aAAa,IAAI,UAAU,GAAG,SAAS,CAAC;CACzC,CAAC;AAIF,wBAAgB,SAAS,CAAC,OAAO,EAAE,YAAY,GAAG,WAAW,CAoC5D;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,aAAa,CAqDhF"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { BadgrToolError, createReport, createRequestId } from "badgr-shared";
|
|
2
|
+
const DEFAULT_BASE_URL = "https://api.aibadgr.com/v1";
|
|
3
|
+
export function withGuard(options) {
|
|
4
|
+
const maxRetries = options.maxRetries ?? 2;
|
|
5
|
+
const timeoutMs = options.timeoutMs ?? 30_000;
|
|
6
|
+
let spentUsd = 0;
|
|
7
|
+
let lastReport;
|
|
8
|
+
const guarded = async function guarded(fn) {
|
|
9
|
+
const requestId = createRequestId("llm");
|
|
10
|
+
let attempt = 0;
|
|
11
|
+
let lastError;
|
|
12
|
+
while (attempt <= maxRetries) {
|
|
13
|
+
attempt += 1;
|
|
14
|
+
assertSpendCap(spentUsd, options.maxSpendUsd, options.estimateCostUsd?.(attempt) ?? 0);
|
|
15
|
+
try {
|
|
16
|
+
const result = await withTimeout(Promise.resolve().then(fn), timeoutMs);
|
|
17
|
+
const costUsd = options.estimateCostUsd?.(attempt) ?? 0;
|
|
18
|
+
assertSpendCap(spentUsd, options.maxSpendUsd, costUsd);
|
|
19
|
+
spentUsd += costUsd;
|
|
20
|
+
lastReport = buildGuardReport("passed", requestId, attempt, spentUsd, undefined);
|
|
21
|
+
writeReceipt(options.receipt ?? "terminal", { requestId, attempts: attempt, status: "completed", costUsd, spentUsd }, lastReport);
|
|
22
|
+
return result;
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
lastError = error;
|
|
26
|
+
if (attempt > maxRetries || !isRetryableFailure(error))
|
|
27
|
+
break;
|
|
28
|
+
await sleep(retryDelayFromError(error) ?? 250 * attempt);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
lastReport = buildGuardReport("failed", requestId, attempt, spentUsd, lastError);
|
|
32
|
+
writeLocalOnlyFailure(lastError, maxRetries, options.receipt ?? "terminal", lastReport);
|
|
33
|
+
throw new BadgrToolError(`Primary provider failed after ${maxRetries} retries. Cause: ${failureCause(lastError)}`, "LLM_GUARD_REQUEST_FAILED", { requestId, attempts: attempt });
|
|
34
|
+
};
|
|
35
|
+
guarded.getSpentUsd = () => spentUsd;
|
|
36
|
+
guarded.getLastReport = () => lastReport;
|
|
37
|
+
return guarded;
|
|
38
|
+
}
|
|
39
|
+
export function createGuardedClient(options) {
|
|
40
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
41
|
+
const maxRetries = options.maxRetries ?? 2;
|
|
42
|
+
const timeoutMs = options.timeoutMs ?? 30_000;
|
|
43
|
+
const baseUrl = options.baseUrl ?? DEFAULT_BASE_URL;
|
|
44
|
+
let spentUsd = 0;
|
|
45
|
+
let lastReport;
|
|
46
|
+
async function request(path, init = {}) {
|
|
47
|
+
const requestId = createRequestId("llm");
|
|
48
|
+
assertSpendCap(spentUsd, options.maxSpendUsd, init.estimatedCostUsd ?? 0);
|
|
49
|
+
let attempt = 0;
|
|
50
|
+
let lastError;
|
|
51
|
+
while (attempt <= maxRetries) {
|
|
52
|
+
attempt += 1;
|
|
53
|
+
const controller = new AbortController();
|
|
54
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
55
|
+
try {
|
|
56
|
+
const response = await fetchImpl(toUrl(baseUrl, path), {
|
|
57
|
+
...init,
|
|
58
|
+
signal: controller.signal,
|
|
59
|
+
headers: {
|
|
60
|
+
...(options.apiKey ? { authorization: `Bearer ${options.apiKey}` } : {}),
|
|
61
|
+
"x-request-id": requestId,
|
|
62
|
+
...(init.headers ?? {}),
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
clearTimeout(timer);
|
|
66
|
+
if (shouldRetry(response.status) && attempt <= maxRetries) {
|
|
67
|
+
await sleep(retryDelayMs(response));
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
const costUsd = readCostUsd(response);
|
|
71
|
+
assertSpendCap(spentUsd, options.maxSpendUsd, costUsd);
|
|
72
|
+
spentUsd += costUsd;
|
|
73
|
+
lastReport = buildGuardReport(response.ok ? "passed" : "failed", requestId, attempt, spentUsd, response.status);
|
|
74
|
+
writeReceipt(options.receipt ?? "terminal", { requestId, attempts: attempt, status: response.status, costUsd, spentUsd }, lastReport);
|
|
75
|
+
return response;
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
clearTimeout(timer);
|
|
79
|
+
lastError = error;
|
|
80
|
+
if (attempt > maxRetries || !isTransientError(error))
|
|
81
|
+
break;
|
|
82
|
+
await sleep(250 * attempt);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
lastReport = buildGuardReport("failed", requestId, attempt, spentUsd, lastError);
|
|
86
|
+
writeLocalOnlyFailure(lastError, maxRetries, options.receipt ?? "terminal", lastReport);
|
|
87
|
+
throw new BadgrToolError(`AI request ${requestId} failed after ${attempt} attempts: ${String(lastError)}`, "LLM_GUARD_REQUEST_FAILED", { requestId, attempts: attempt });
|
|
88
|
+
}
|
|
89
|
+
return { request, getSpentUsd: () => spentUsd, getLastReport: () => lastReport };
|
|
90
|
+
}
|
|
91
|
+
function toUrl(baseUrl, path) {
|
|
92
|
+
if (/^https?:\/\//.test(path))
|
|
93
|
+
return path;
|
|
94
|
+
return `${baseUrl.replace(/\/$/, "")}/${path.replace(/^\//, "")}`;
|
|
95
|
+
}
|
|
96
|
+
function shouldRetry(status) {
|
|
97
|
+
return status === 429 || status === 408 || (status >= 500 && status <= 599);
|
|
98
|
+
}
|
|
99
|
+
function retryDelayMs(response) {
|
|
100
|
+
const retryAfter = response.headers.get("retry-after");
|
|
101
|
+
if (!retryAfter)
|
|
102
|
+
return 500;
|
|
103
|
+
const seconds = Number(retryAfter);
|
|
104
|
+
if (Number.isFinite(seconds))
|
|
105
|
+
return Math.max(0, seconds * 1000);
|
|
106
|
+
const dateMs = Date.parse(retryAfter);
|
|
107
|
+
return Number.isFinite(dateMs) ? Math.max(0, dateMs - Date.now()) : 500;
|
|
108
|
+
}
|
|
109
|
+
function retryDelayFromError(error) {
|
|
110
|
+
const retryAfter = readErrorStatus(error)?.retryAfter;
|
|
111
|
+
if (retryAfter === undefined)
|
|
112
|
+
return undefined;
|
|
113
|
+
const seconds = Number(retryAfter);
|
|
114
|
+
if (Number.isFinite(seconds))
|
|
115
|
+
return Math.max(0, seconds * 1000);
|
|
116
|
+
const dateMs = Date.parse(String(retryAfter));
|
|
117
|
+
return Number.isFinite(dateMs) ? Math.max(0, dateMs - Date.now()) : undefined;
|
|
118
|
+
}
|
|
119
|
+
function readCostUsd(response) {
|
|
120
|
+
const header = response.headers.get("x-badgr-cost-usd") ?? response.headers.get("x-cost-usd");
|
|
121
|
+
const parsed = header ? Number(header) : 0;
|
|
122
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
123
|
+
}
|
|
124
|
+
function assertSpendCap(spentUsd, maxSpendUsd, nextCostUsd) {
|
|
125
|
+
if (maxSpendUsd !== undefined && spentUsd + nextCostUsd > maxSpendUsd) {
|
|
126
|
+
throw new BadgrToolError(`Spend cap exceeded: $${(spentUsd + nextCostUsd).toFixed(4)} would exceed $${maxSpendUsd.toFixed(4)}`, "LLM_GUARD_SPEND_CAP", { spentUsd, nextCostUsd, maxSpendUsd });
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function isRetryableFailure(error) {
|
|
130
|
+
const status = readErrorStatus(error)?.status;
|
|
131
|
+
return status !== undefined ? shouldRetry(status) : isTransientError(error);
|
|
132
|
+
}
|
|
133
|
+
function isTransientError(error) {
|
|
134
|
+
return error instanceof Error && (error.name === "AbortError" || /timeout|network|fetch/i.test(error.message));
|
|
135
|
+
}
|
|
136
|
+
function readErrorStatus(error) {
|
|
137
|
+
if (!error || typeof error !== "object")
|
|
138
|
+
return undefined;
|
|
139
|
+
const candidate = error;
|
|
140
|
+
const status = typeof candidate.status === "number" ? candidate.status : typeof candidate.response?.status === "number" ? candidate.response.status : undefined;
|
|
141
|
+
const retryAfter = candidate.retryAfter ?? candidate.headers?.get("retry-after") ?? candidate.response?.headers?.get("retry-after") ?? undefined;
|
|
142
|
+
return { status, retryAfter: typeof retryAfter === "string" || typeof retryAfter === "number" ? retryAfter : undefined };
|
|
143
|
+
}
|
|
144
|
+
function failureCause(error) {
|
|
145
|
+
const status = readErrorStatus(error)?.status;
|
|
146
|
+
if (status)
|
|
147
|
+
return `HTTP ${status}`;
|
|
148
|
+
return error instanceof Error ? error.message : String(error);
|
|
149
|
+
}
|
|
150
|
+
function withTimeout(promise, timeoutMs) {
|
|
151
|
+
return new Promise((resolve, reject) => {
|
|
152
|
+
const timer = setTimeout(() => reject(new DOMException("timeout", "AbortError")), timeoutMs);
|
|
153
|
+
promise.then((value) => { clearTimeout(timer); resolve(value); }, (error) => { clearTimeout(timer); reject(error); });
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
function sleep(ms) {
|
|
157
|
+
return new Promise((resolve) => setTimeout(resolve, Math.min(ms, 30_000)));
|
|
158
|
+
}
|
|
159
|
+
function buildGuardReport(status, requestId, attempts, spentUsd, cause) {
|
|
160
|
+
const failed = status === "failed";
|
|
161
|
+
return createReport({
|
|
162
|
+
tool: "llm-guard",
|
|
163
|
+
reportId: `rpt_${requestId}`,
|
|
164
|
+
status,
|
|
165
|
+
summary: failed ? "Primary provider failed in local-only mode" : "Local LLM guard completed the request",
|
|
166
|
+
findings: [{ severity: failed ? "error" : "info", code: failed ? "LLM_PRIMARY_PROVIDER_FAILED" : "LLM_LOCAL_GUARD_OK", message: failed ? `Cause: ${failureCause(cause)}` : `Request completed after ${attempts} attempt(s).` }],
|
|
167
|
+
recommendedActions: failed ? ["Enable provider fallback with AI Badgr: baseURL https://api.aibadgr.com/v1"] : ["Keep local retry and spend caps enabled"],
|
|
168
|
+
nextCommand: "Set baseURL to https://api.aibadgr.com/v1 for provider fallback",
|
|
169
|
+
actionUrl: "https://aibadgr.com/dashboard",
|
|
170
|
+
metadata: { requestId, attempts, spentUsd },
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
function writeReceipt(mode, receipt, report) {
|
|
174
|
+
if (mode === "none")
|
|
175
|
+
return;
|
|
176
|
+
if (mode === "json") {
|
|
177
|
+
console.log(JSON.stringify(report, null, 2));
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
console.log(`AI Badgr receipt ${receipt.requestId}: status ${receipt.status}, attempts ${receipt.attempts}, cost $${receipt.costUsd.toFixed(4)}, session spend $${receipt.spentUsd.toFixed(4)}`);
|
|
181
|
+
}
|
|
182
|
+
function writeLocalOnlyFailure(error, maxRetries, mode, report) {
|
|
183
|
+
if (mode === "none")
|
|
184
|
+
return;
|
|
185
|
+
if (mode === "json") {
|
|
186
|
+
console.log(JSON.stringify(report, null, 2));
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
console.log(`Primary provider failed after ${maxRetries} retries.`);
|
|
190
|
+
console.log(`Cause: ${failureCause(error)}`);
|
|
191
|
+
console.log("Fallback unavailable in local-only mode.");
|
|
192
|
+
console.log("");
|
|
193
|
+
console.log("Enable provider fallback with AI Badgr:");
|
|
194
|
+
console.log('baseURL: "https://api.aibadgr.com/v1"');
|
|
195
|
+
}
|
|
196
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "badgr-llm-guard",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Thin guarded AI API client with retries, Retry-After support, spend caps, request IDs, and receipts.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"bin": { "badgr-llm-guard": "dist/cli.js" },
|
|
9
|
+
"exports": { ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" } },
|
|
10
|
+
"files": ["dist", "README.md"],
|
|
11
|
+
"scripts": { "build": "tsc -b", "typecheck": "tsc -b --pretty false", "test": "vitest run" },
|
|
12
|
+
"dependencies": { "badgr-shared": "0.1.1" },
|
|
13
|
+
"engines": { "node": ">=18.0.0" },
|
|
14
|
+
"keywords": ["llm", "retry", "rate-limit", "timeout", "spend-cap", "gateway"],
|
|
15
|
+
"license": "MIT"
|
|
16
|
+
}
|