ai-resilience-gateway 1.0.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/LICENSE +21 -0
- package/README.md +205 -0
- package/dist/cascade.d.ts +10 -0
- package/dist/cascade.d.ts.map +1 -0
- package/dist/cascade.js +95 -0
- package/dist/cascade.js.map +1 -0
- package/dist/errors.d.ts +9 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +19 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/providers.d.ts +5 -0
- package/dist/providers.d.ts.map +1 -0
- package/dist/providers.js +22 -0
- package/dist/providers.js.map +1 -0
- package/dist/retry.d.ts +28 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +33 -0
- package/dist/retry.js.map +1 -0
- package/dist/types.d.ts +33 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +56 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Vasil Tomov
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# ai-resilience-gateway
|
|
2
|
+
|
|
3
|
+
Resilient LLM chat completions with **provider fallback**, **retries**, and **timeouts**.
|
|
4
|
+
Zero runtime dependencies — a TypeScript library for learning production LLM client patterns and showcasing them in a portfolio.
|
|
5
|
+
|
|
6
|
+
When your primary provider rate-limits you or goes down, the gateway falls through to the next one so a single hiccup doesn’t take down your feature.
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
Groq ──fails──▶ Ollama (local)
|
|
10
|
+
│ │
|
|
11
|
+
retries+backoff retries+backoff
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Presets also include `openrouter()` if you set `OPENROUTER_API_KEY`. You can order any providers you want, for example `[groq(), openrouter(), ollama()]`.
|
|
15
|
+
|
|
16
|
+
## Why
|
|
17
|
+
|
|
18
|
+
Most “call an LLM” code is a single `fetch` with no retry, no timeout, and no plan B:
|
|
19
|
+
|
|
20
|
+
- **429** during traffic spikes → the feature just errors out
|
|
21
|
+
- a **provider outage** → downtime for anything LLM-backed
|
|
22
|
+
- a **hung request** with no timeout → work piles up until the process struggles
|
|
23
|
+
|
|
24
|
+
This library handles all three:
|
|
25
|
+
|
|
26
|
+
- exponential backoff on retryable failures (`429` / `5xx` / network / timeout)
|
|
27
|
+
- a hard per-request timeout via `AbortController`
|
|
28
|
+
- ordered fallback across any number of OpenAI-compatible providers
|
|
29
|
+
|
|
30
|
+
## Requirements
|
|
31
|
+
|
|
32
|
+
- **Node 18+** (uses the global `fetch` API)
|
|
33
|
+
|
|
34
|
+
## Setup
|
|
35
|
+
|
|
36
|
+
```sh
|
|
37
|
+
git clone https://github.com/vtomov90/AI-resilience-gateway.git
|
|
38
|
+
cd AI-resilience-gateway
|
|
39
|
+
npm install
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Usage
|
|
43
|
+
|
|
44
|
+
Install the package:
|
|
45
|
+
|
|
46
|
+
```sh
|
|
47
|
+
npm install ai-resilience-gateway
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
Then use it:
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
import { LLMCascade, groq, ollama } from "ai-resilience-gateway";
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
const llm = new LLMCascade([groq(), ollama()], {
|
|
59
|
+
retries: 2,
|
|
60
|
+
timeoutMs: 30_000,
|
|
61
|
+
onFallback: (provider, err) =>
|
|
62
|
+
console.warn(`[gateway] ${provider} failed:`, err.message),
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const result = await llm.complete([
|
|
66
|
+
{ role: "system", content: "You are a concise assistant." },
|
|
67
|
+
{ role: "user", content: "Explain retries in one sentence." },
|
|
68
|
+
]);
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
console.log(result.text); // model reply
|
|
72
|
+
console.log(result.provider); // "groq" or "ollama" — who answered
|
|
73
|
+
console.log(result.latencyMs); // e.g. 266
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
### Provider presets
|
|
78
|
+
|
|
79
|
+
Built-in factories (they just return a `Provider` object):
|
|
80
|
+
|
|
81
|
+
| Preset | Default model | API key |
|
|
82
|
+
|--------|---------------|---------|
|
|
83
|
+
| `groq()` | `openai/gpt-oss-20b` | `GROQ_API_KEY` |
|
|
84
|
+
| `openrouter()` | `meta-llama/llama-3.3-70b-instruct` | `OPENROUTER_API_KEY` (optional) |
|
|
85
|
+
| `ollama()` | `llama3.1` | none (local) |
|
|
86
|
+
|
|
87
|
+
Override model or key when needed:
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
groq("openai/gpt-oss-20b", process.env.GROQ_API_KEY);
|
|
91
|
+
openrouter("meta-llama/llama-3.3-70b-instruct", process.env.OPENROUTER_API_KEY);
|
|
92
|
+
ollama("llama3.1", "http://localhost:11434");
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Any OpenAI-compatible endpoint
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
const custom = {
|
|
99
|
+
name: "together",
|
|
100
|
+
url: "https://api.together.xyz/v1/chat/completions",
|
|
101
|
+
model: "some-model-id",
|
|
102
|
+
apiKey: process.env.TOGETHER_API_KEY,
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const llm = new LLMCascade([custom, ollama()]);
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## API
|
|
109
|
+
|
|
110
|
+
### `new LLMCascade(providers, options?)`
|
|
111
|
+
|
|
112
|
+
| Option | Default | Description |
|
|
113
|
+
|--------|---------|-------------|
|
|
114
|
+
| `timeoutMs` | `30000` | Per-request timeout; abort is treated as retryable |
|
|
115
|
+
| `retries` | `2` | Attempts per provider (`1` = no retry) |
|
|
116
|
+
| `backoffMs` | `500` | Base backoff; doubles each retry (`500` → `1000` → …) |
|
|
117
|
+
| `onFallback` | — | `(provider, error) => void` when moving past a provider |
|
|
118
|
+
|
|
119
|
+
### `cascade.complete(messages, options?)`
|
|
120
|
+
|
|
121
|
+
`options`: `{ temperature?, maxTokens?, signal? }`
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
{
|
|
127
|
+
text: string;
|
|
128
|
+
provider: string;
|
|
129
|
+
model: string;
|
|
130
|
+
latencyMs: number;
|
|
131
|
+
usage?: { promptTokens?: number; completionTokens?: number };
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Throws `AllProvidersFailedError` (with a `.causes` map of `provider → Error`) only when every provider is exhausted.
|
|
136
|
+
|
|
137
|
+
## Retry policy
|
|
138
|
+
|
|
139
|
+
**Retryable** (same provider, with backoff):
|
|
140
|
+
|
|
141
|
+
- HTTP `408`, `429`, `500`, `502`, `503`, `504`
|
|
142
|
+
- network errors and timeouts
|
|
143
|
+
|
|
144
|
+
**Non-retryable** (fall through to the next provider immediately):
|
|
145
|
+
|
|
146
|
+
- typical `4xx` like `401`, `403`, `404` — no point hammering a bad API key or unknown URL
|
|
147
|
+
|
|
148
|
+
## Example CLI
|
|
149
|
+
|
|
150
|
+
Create a local `.env` (do **not** commit it):
|
|
151
|
+
|
|
152
|
+
```env
|
|
153
|
+
GROQ_API_KEY=gsk_your_key_here
|
|
154
|
+
OPENROUTER_API_KEY=
|
|
155
|
+
OLLAMA_MODEL=llama3.1
|
|
156
|
+
OLLAMA_BASE_URL=http://localhost:11434
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
Run:
|
|
162
|
+
|
|
163
|
+
```sh
|
|
164
|
+
npm run example -- "Say hello in one sentence."
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
The example tries Groq first (if `GROQ_API_KEY` is set), then Ollama as a local fallback.
|
|
168
|
+
|
|
169
|
+
> Groq model ids change over time and can depend on your account plan. If you get `model_not_found`, list models for your key (`GET https://api.groq.com/openai/v1/models`) and pass an available chat model into `groq("your-model-id")`.
|
|
170
|
+
|
|
171
|
+
## Development
|
|
172
|
+
|
|
173
|
+
```sh
|
|
174
|
+
npm test # Vitest — fetch fully mocked, no network / no API keys
|
|
175
|
+
npm run test:watch
|
|
176
|
+
npm run typecheck
|
|
177
|
+
npm run build
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Project layout
|
|
181
|
+
|
|
182
|
+
```
|
|
183
|
+
src/
|
|
184
|
+
types.ts # ChatMessage, Provider, options, result types
|
|
185
|
+
errors.ts # HttpError, AllProvidersFailedError
|
|
186
|
+
retry.ts # isRetryable, backoff helpers
|
|
187
|
+
cascade.ts # LLMCascade (request → retries → fallback)
|
|
188
|
+
providers.ts # groq / openrouter / ollama presets
|
|
189
|
+
index.ts # public exports
|
|
190
|
+
examples/
|
|
191
|
+
ask.ts # live demo CLI
|
|
192
|
+
tests/ # unit tests with mocked fetch
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## What this project demonstrates
|
|
196
|
+
|
|
197
|
+
- OpenAI-compatible chat completions (`choices[0].message.content`)
|
|
198
|
+
- `fetch` + `AbortController` for timeouts and external abort
|
|
199
|
+
- Retry vs fallback as separate layers
|
|
200
|
+
- Typed custom errors for debugging multi-provider failure
|
|
201
|
+
- Vitest with mocked `fetch` and fake timers
|
|
202
|
+
|
|
203
|
+
## License
|
|
204
|
+
|
|
205
|
+
MIT
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { CascadeOptions, ChatMessage, CompletionOptions, CompletionResult, Provider } from "./types.js";
|
|
2
|
+
export declare class LLMCascade {
|
|
3
|
+
private readonly providers;
|
|
4
|
+
private readonly opts;
|
|
5
|
+
constructor(providers: Provider[], options?: CascadeOptions);
|
|
6
|
+
complete(messages: ChatMessage[], options?: CompletionOptions): Promise<CompletionResult>;
|
|
7
|
+
private completeWithRetries;
|
|
8
|
+
private request;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=cascade.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cascade.d.ts","sourceRoot":"","sources":["../src/cascade.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,cAAc,EACd,WAAW,EACX,iBAAiB,EACjB,gBAAgB,EAChB,QAAQ,EACT,MAAM,YAAY,CAAC;AAEpB,qBAAa,UAAU;IACnB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAa;IACvC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAoF;gBAE7F,SAAS,EAAE,QAAQ,EAAE,EAAE,OAAO,GAAE,cAAmB;IAczD,QAAQ,CACV,QAAQ,EAAE,WAAW,EAAE,EACvB,OAAO,GAAE,iBAAsB,GAChC,OAAO,CAAE,gBAAgB,CAAC;YAgBf,mBAAmB;YAsBnB,OAAO;CAqExB"}
|
package/dist/cascade.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { AllProvidersFailedError, HttpError } from "./errors.js";
|
|
2
|
+
import { isRetryable, sleep } from "./retry.js";
|
|
3
|
+
export class LLMCascade {
|
|
4
|
+
providers;
|
|
5
|
+
opts;
|
|
6
|
+
constructor(providers, options = {}) {
|
|
7
|
+
if (providers.length === 0) {
|
|
8
|
+
throw new Error("LLMCascade requires at least one provider");
|
|
9
|
+
}
|
|
10
|
+
this.providers = providers;
|
|
11
|
+
this.opts = {
|
|
12
|
+
timeoutMs: options.timeoutMs ?? 30_000,
|
|
13
|
+
retries: options.retries ?? 2,
|
|
14
|
+
backoffMs: options.backoffMs ?? 500,
|
|
15
|
+
onFallback: options.onFallback,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
async complete(messages, options = {}) {
|
|
19
|
+
const causes = {};
|
|
20
|
+
for (const provider of this.providers) {
|
|
21
|
+
try {
|
|
22
|
+
return await this.completeWithRetries(provider, messages, options);
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
26
|
+
causes[provider.name] = error;
|
|
27
|
+
this.opts.onFallback?.(provider.name, error);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
throw new AllProvidersFailedError(causes);
|
|
31
|
+
}
|
|
32
|
+
async completeWithRetries(provider, messages, options) {
|
|
33
|
+
let lastError = new Error("unreachable");
|
|
34
|
+
for (let attempt = 0; attempt < this.opts.retries; attempt++) {
|
|
35
|
+
if (attempt > 0) {
|
|
36
|
+
await sleep(this.opts.backoffMs * 2 ** (attempt - 1));
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
return await this.request(provider, messages, options);
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
lastError = err instanceof Error ? err : new Error(String(err));
|
|
43
|
+
if (!isRetryable(lastError))
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
throw lastError;
|
|
48
|
+
}
|
|
49
|
+
async request(provider, messages, options) {
|
|
50
|
+
const controller = new AbortController();
|
|
51
|
+
const timer = setTimeout(() => controller.abort(new Error(`timeout after ${this.opts.timeoutMs}ms`)), this.opts.timeoutMs);
|
|
52
|
+
options.signal?.addEventListener("abort", () => controller.abort(options.signal.reason), { once: true });
|
|
53
|
+
const started = Date.now();
|
|
54
|
+
try {
|
|
55
|
+
const res = await fetch(provider.url, {
|
|
56
|
+
method: "POST",
|
|
57
|
+
signal: controller.signal,
|
|
58
|
+
headers: {
|
|
59
|
+
"Content-Type": "application/json",
|
|
60
|
+
...(provider.apiKey ? { Authorization: `Bearer ${provider.apiKey}` } : {}),
|
|
61
|
+
...provider.headers,
|
|
62
|
+
},
|
|
63
|
+
body: JSON.stringify({
|
|
64
|
+
model: provider.model,
|
|
65
|
+
messages,
|
|
66
|
+
temperature: options.temperature ?? 0.7,
|
|
67
|
+
...(options.maxTokens ? { max_tokens: options.maxTokens } : {})
|
|
68
|
+
}),
|
|
69
|
+
});
|
|
70
|
+
if (!res.ok) {
|
|
71
|
+
const body = await res.text().catch(() => "");
|
|
72
|
+
throw new HttpError(res.status, `${provider.name} HTTP ${res.status}: ${body.slice(0, 200)}`);
|
|
73
|
+
}
|
|
74
|
+
const data = (await res.json());
|
|
75
|
+
const text = data.choices?.[0]?.message?.content;
|
|
76
|
+
if (typeof text !== "string") {
|
|
77
|
+
throw new HttpError(502, `${provider.name} returned no completion text`);
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
text,
|
|
81
|
+
provider: provider.name,
|
|
82
|
+
model: provider.model,
|
|
83
|
+
latencyMs: Date.now() - started,
|
|
84
|
+
usage: {
|
|
85
|
+
promptTokens: data.usage?.prompt_tokens,
|
|
86
|
+
completionTokens: data.usage?.completion_tokens,
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
finally {
|
|
91
|
+
clearTimeout(timer);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=cascade.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cascade.js","sourceRoot":"","sources":["../src/cascade.ts"],"names":[],"mappings":"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"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare class HttpError extends Error {
|
|
2
|
+
readonly status: number;
|
|
3
|
+
constructor(status: number, message: string);
|
|
4
|
+
}
|
|
5
|
+
export declare class AllProvidersFailedError extends Error {
|
|
6
|
+
readonly causes: Record<string, Error>;
|
|
7
|
+
constructor(causes: Record<string, Error>);
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,SAAU,SAAQ,KAAK;aAEZ,MAAM,EAAE,MAAM;gBAAd,MAAM,EAAE,MAAM,EAC9B,OAAO,EAAC,MAAM;CAKrB;AAGD,qBAAa,uBAAwB,SAAQ,KAAK;aAE1B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC;gBAA7B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC;CAUpD"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export class HttpError extends Error {
|
|
2
|
+
status;
|
|
3
|
+
constructor(status, message) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.status = status;
|
|
6
|
+
this.name = "HttpError";
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export class AllProvidersFailedError extends Error {
|
|
10
|
+
causes;
|
|
11
|
+
constructor(causes) {
|
|
12
|
+
super("All LLM providers failed: " +
|
|
13
|
+
Object.entries(causes)
|
|
14
|
+
.map(([name, err]) => `${name} (${err.message})`));
|
|
15
|
+
this.causes = causes;
|
|
16
|
+
this.name = "AllProvidersFailedError";
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,SAAU,SAAQ,KAAK;IAEZ;IADpB,YACoB,MAAc,EAC9B,OAAc;QAEd,KAAK,CAAC,OAAO,CAAC,CAAC;QAHC,WAAM,GAAN,MAAM,CAAQ;QAI9B,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC;IAC5B,CAAC;CACJ;AAGD,MAAM,OAAO,uBAAwB,SAAQ,KAAK;IAE1B;IADpB,YACoB,MAA6B;QAE7C,KAAK,CACD,4BAA4B;YACxB,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;iBACjB,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,KAAK,GAAG,CAAC,OAAO,GAAG,CAAC,CAC5D,CAAC;QANc,WAAM,GAAN,MAAM,CAAuB;QAQ7C,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAC;IAC1C,CAAC;CACJ"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { LLMCascade } from "./cascade.js";
|
|
2
|
+
export { AllProvidersFailedError, HttpError } from "./errors.js";
|
|
3
|
+
export { groq, ollama, openrouter } from "./providers.js";
|
|
4
|
+
export { isRetryable, RETRYABLE_STATUS, sleep } from "./retry.js";
|
|
5
|
+
export type { CascadeOptions, ChatMessage, CompletionOptions, CompletionResult, Provider, } from "./types.js";
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,uBAAuB,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAClE,YAAY,EACV,cAAc,EACd,WAAW,EACX,iBAAiB,EACjB,gBAAgB,EAChB,QAAQ,GACT,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { LLMCascade } from "./cascade.js";
|
|
2
|
+
export { AllProvidersFailedError, HttpError } from "./errors.js";
|
|
3
|
+
export { groq, ollama, openrouter } from "./providers.js";
|
|
4
|
+
export { isRetryable, RETRYABLE_STATUS, sleep } from "./retry.js";
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,uBAAuB,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Provider } from "./types.js";
|
|
2
|
+
export declare const groq: (model?: string, apiKey?: string | undefined) => Provider;
|
|
3
|
+
export declare const openrouter: (model?: string, apiKey?: string | undefined) => Provider;
|
|
4
|
+
export declare const ollama: (model?: string, baseUrl?: string) => Provider;
|
|
5
|
+
//# sourceMappingURL=providers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"providers.d.ts","sourceRoot":"","sources":["../src/providers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,eAAO,MAAM,IAAI,GACb,cAA4B,EAC5B,2BAAiC,KAClC,QAKD,CAAA;AAEF,eAAO,MAAM,UAAU,GACnB,cAA2C,EAC3C,2BAAuC,KACxC,QASD,CAAA;AAEF,eAAO,MAAM,MAAM,GACf,cAAkB,EAClB,gBAAkC,KACnC,QAID,CAAA"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export const groq = (model = "openai/gpt-oss-20b", apiKey = process.env.GROQ_API_KEY) => ({
|
|
2
|
+
name: "groq",
|
|
3
|
+
url: "https://api.groq.com/openai/v1/chat/completions",
|
|
4
|
+
model,
|
|
5
|
+
apiKey
|
|
6
|
+
});
|
|
7
|
+
export const openrouter = (model = "meta-llama/llama-3.3-70b-instruct", apiKey = process.env.OPENROUTER_API_KEY) => ({
|
|
8
|
+
name: "openrouter",
|
|
9
|
+
url: "https://openrouter.ai/api/v1/chat/completions",
|
|
10
|
+
model,
|
|
11
|
+
apiKey,
|
|
12
|
+
headers: {
|
|
13
|
+
"HTTP-Referer": "https://github.com/ai-resilience-gateway",
|
|
14
|
+
"X-Title": "ai-resilience-gateway",
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
export const ollama = (model = "llama3.1", baseUrl = "http://localhost:11434") => ({
|
|
18
|
+
name: "ollama",
|
|
19
|
+
url: `${baseUrl}/v1/chat/completions`,
|
|
20
|
+
model,
|
|
21
|
+
});
|
|
22
|
+
//# sourceMappingURL=providers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"providers.js","sourceRoot":"","sources":["../src/providers.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,IAAI,GAAG,CAChB,KAAK,GAAG,oBAAoB,EAC5B,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,EACzB,EAAE,CAAC,CAAC;IACZ,IAAI,EAAE,MAAM;IACZ,GAAG,EAAG,iDAAiD;IACvD,KAAK;IACL,MAAM;CACT,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,UAAU,GAAG,CACtB,KAAK,GAAG,mCAAmC,EAC3C,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAC/B,EAAE,CAAC,CAAC;IACZ,IAAI,EAAG,YAAY;IACnB,GAAG,EAAE,+CAA+C;IACpD,KAAK;IACL,MAAM;IACN,OAAO,EAAE;QACL,cAAc,EAAE,0CAA0C;QAC1D,SAAS,EAAE,uBAAuB;KACrC;CACJ,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,MAAM,GAAG,CAClB,KAAK,GAAG,UAAU,EAClB,OAAO,GAAG,wBAAwB,EAC1B,EAAE,CAAC,CAAC;IACZ,IAAI,EAAE,QAAQ;IACd,GAAG,EAAE,GAAG,OAAO,sBAAsB;IACrC,KAAK;CACR,CAAC,CAAA"}
|
package/dist/retry.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A set of HTTP status codes that indicate temporary or transient failures.
|
|
3
|
+
*
|
|
4
|
+
* If a provider fails with one of these statuses, it means the issue might resolve
|
|
5
|
+
* if we retry the request (e.g., rate limits cooling down or server recovering).
|
|
6
|
+
* Non-listed statuses (like 401 Unauthorized or 404 Not Found) are permanent and
|
|
7
|
+
* should fail immediately without retrying on the same provider.
|
|
8
|
+
*/
|
|
9
|
+
export declare const RETRYABLE_STATUS: Set<number>;
|
|
10
|
+
/**
|
|
11
|
+
* Pauses execution for a specified duration.
|
|
12
|
+
*
|
|
13
|
+
* Uses a Promise wrapping `setTimeout` (forming a closure over `ms`) so it can
|
|
14
|
+
* be paused using `await`.
|
|
15
|
+
*
|
|
16
|
+
* @param ms - The number of milliseconds to pause execution.
|
|
17
|
+
* @returns A Promise that resolves after the specified delay.
|
|
18
|
+
|
|
19
|
+
*/
|
|
20
|
+
export declare const sleep: (ms: number) => Promise<unknown>;
|
|
21
|
+
/**
|
|
22
|
+
* Determines whether a failed request should be retried on the same provider.
|
|
23
|
+
*
|
|
24
|
+
* @param err - The Error caught during a provider request attempt.
|
|
25
|
+
* @returns `true` if the failure is temporary and safe to retry; `false` otherwise.
|
|
26
|
+
*/
|
|
27
|
+
export declare function isRetryable(err: Error): boolean;
|
|
28
|
+
//# sourceMappingURL=retry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../src/retry.ts"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AAEH,eAAO,MAAM,gBAAgB,aAA2C,CAAC;AAIzE;;;;;;;;;EASE;AAEF,eAAO,MAAM,KAAK,GAAI,IAAI,MAAM,qBAA0C,CAAC;AAG3E;;;;;GAKG;AAEH,wBAAgB,WAAW,CAAC,GAAG,EAAE,KAAK,GAAG,OAAO,CAG/C"}
|
package/dist/retry.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { HttpError } from "./errors.js";
|
|
2
|
+
/**
|
|
3
|
+
* A set of HTTP status codes that indicate temporary or transient failures.
|
|
4
|
+
*
|
|
5
|
+
* If a provider fails with one of these statuses, it means the issue might resolve
|
|
6
|
+
* if we retry the request (e.g., rate limits cooling down or server recovering).
|
|
7
|
+
* Non-listed statuses (like 401 Unauthorized or 404 Not Found) are permanent and
|
|
8
|
+
* should fail immediately without retrying on the same provider.
|
|
9
|
+
*/
|
|
10
|
+
export const RETRYABLE_STATUS = new Set([408, 429, 500, 502, 503, 504]);
|
|
11
|
+
/**
|
|
12
|
+
* Pauses execution for a specified duration.
|
|
13
|
+
*
|
|
14
|
+
* Uses a Promise wrapping `setTimeout` (forming a closure over `ms`) so it can
|
|
15
|
+
* be paused using `await`.
|
|
16
|
+
*
|
|
17
|
+
* @param ms - The number of milliseconds to pause execution.
|
|
18
|
+
* @returns A Promise that resolves after the specified delay.
|
|
19
|
+
|
|
20
|
+
*/
|
|
21
|
+
export const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
22
|
+
/**
|
|
23
|
+
* Determines whether a failed request should be retried on the same provider.
|
|
24
|
+
*
|
|
25
|
+
* @param err - The Error caught during a provider request attempt.
|
|
26
|
+
* @returns `true` if the failure is temporary and safe to retry; `false` otherwise.
|
|
27
|
+
*/
|
|
28
|
+
export function isRetryable(err) {
|
|
29
|
+
if (err instanceof HttpError)
|
|
30
|
+
return RETRYABLE_STATUS.has(err.status);
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=retry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.js","sourceRoot":"","sources":["../src/retry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAGxC;;;;;;;GAOG;AAEH,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;AAIzE;;;;;;;;;EASE;AAEF,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,EAAU,EAAE,EAAE,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;AAG3E;;;;;GAKG;AAEH,MAAM,UAAU,WAAW,CAAC,GAAU;IAClC,IAAI,GAAG,YAAY,SAAS;QAAE,OAAO,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;IACrE,OAAO,IAAI,CAAC;AAChB,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export interface ChatMessage {
|
|
2
|
+
role: "system" | "user" | "assistant";
|
|
3
|
+
content: string;
|
|
4
|
+
}
|
|
5
|
+
export interface Provider {
|
|
6
|
+
name: string;
|
|
7
|
+
url: string;
|
|
8
|
+
model: string;
|
|
9
|
+
apiKey?: string;
|
|
10
|
+
headers?: Record<string, string>;
|
|
11
|
+
}
|
|
12
|
+
export interface CascadeOptions {
|
|
13
|
+
timeoutMs?: number;
|
|
14
|
+
retries?: number;
|
|
15
|
+
backoffMs?: number;
|
|
16
|
+
onFallback?: (provider: string, error: Error) => void;
|
|
17
|
+
}
|
|
18
|
+
export interface CompletionOptions {
|
|
19
|
+
temperature?: number;
|
|
20
|
+
maxTokens?: number;
|
|
21
|
+
signal?: AbortSignal;
|
|
22
|
+
}
|
|
23
|
+
export interface CompletionResult {
|
|
24
|
+
text: string;
|
|
25
|
+
provider: string;
|
|
26
|
+
model: string;
|
|
27
|
+
latencyMs: number;
|
|
28
|
+
usage?: {
|
|
29
|
+
promptTokens?: number;
|
|
30
|
+
completionTokens?: number;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IACxB,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,QAAQ;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,cAAc;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CACzD;AAED,MAAM,WAAW,iBAAiB;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE;QAAE,YAAY,CAAC,EAAE,MAAM,CAAC;QAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAEhE"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ai-resilience-gateway",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Resilient LLM chat completions with provider fallback, retries, and timeouts. Zero runtime dependencies.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsc -p tsconfig.build.json",
|
|
19
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
20
|
+
"test": "vitest run",
|
|
21
|
+
"test:watch": "vitest",
|
|
22
|
+
"example": "tsx examples/ask.ts",
|
|
23
|
+
"prepublishOnly": "npm test && npm run build"
|
|
24
|
+
},
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=18"
|
|
27
|
+
},
|
|
28
|
+
"keywords": [
|
|
29
|
+
"llm",
|
|
30
|
+
"openai-compatible",
|
|
31
|
+
"groq",
|
|
32
|
+
"openrouter",
|
|
33
|
+
"ollama",
|
|
34
|
+
"fallback",
|
|
35
|
+
"retry",
|
|
36
|
+
"typescript"
|
|
37
|
+
],
|
|
38
|
+
"author": "Vasil Tomov",
|
|
39
|
+
"license": "MIT",
|
|
40
|
+
"repository": {
|
|
41
|
+
"type": "git",
|
|
42
|
+
"url": "git+https://github.com/vtomov90/AI-resilience-gateway.git"
|
|
43
|
+
},
|
|
44
|
+
"bugs": {
|
|
45
|
+
"url": "https://github.com/vtomov90/AI-resilience-gateway/issues"
|
|
46
|
+
},
|
|
47
|
+
"homepage": "https://github.com/vtomov90/AI-resilience-gateway#readme",
|
|
48
|
+
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@types/node": "^26.6.1",
|
|
51
|
+
"dotenv": "^17.4.2",
|
|
52
|
+
"tsx": "^4.23.13",
|
|
53
|
+
"typescript": "^5.7.2",
|
|
54
|
+
"vitest": "^4.1.11"
|
|
55
|
+
}
|
|
56
|
+
}
|